Published by FelonEkonom almost 2 years ago
This release includes mostly API improvements. For some of them, breaking changes were necessary. To facilitate migration, we prepared the updating guide that should help adjust your code to the changes.
The following actions have changed:
Membrane.Element.Action.caps_t
-> Membrane.Element.Action.stream_format_t
(#468)Membrane.Bin.Action.notify_t
-> Membrane.Bin.Action.notify_parent_t
(#415)Membrane.Element.Action.notify_t
-> Membrane.Element.Action.notify_parent_t
(#415)Membrane.Pipeline.Action.forward_t
-> Membrane.Pipeline.Action.notify_child_t
(#415)Membrane.Bin.Action.forward_t
-> Membrane.Bin.Action.notify_child_t
(#415)From now on, the callbacks should return an {actions, state}
tuple. Returning errors is no longer supported - if a component can't recover from an error, it should raise (#471)
Furthermore, the following callbacks have changed:
handle_stopped_to_prepared/2
-> handle_setup/2
in all components (#432)Membrane.Element.Base.handle_caps/4
-> Membrane.Element.Base.handle_stream_format/4
(#432)handle_element_start_of_stream/3
-> handle_element_start_of_stream/4
and handle_element_end_of_stream/3
-> handle_element_end_of_stream/4
in pipelines and bins (#417)handle_init/1
-> handle_init/2
in all components (#432)handle_other/3
-> handle_info/3
in all components (#415)handle_notification/4
-> handle_child_notification/4
in pipelines and bins (#415)The following callbacks have been removed:
handle_playing_to_prepared/2
(#432)handle_prepared_to_stopped/2
(#432)handle_shutdown/2
(#432)Instead, Membrane.ResourceGuard
, Membrane.UtilitySupervisor
or handle_terminate_request/2
should be used.
What is more, messages sent with the :notify_parent
action (former :notify
action), are handled in a separate handle_parent_notification/3
callback instead of being handled in handle_other/3
, along with messages sent from any other processes. (#415)
In v0.11
:caps
option passed to def_input_pad/2
or def_output_pad/2
has been deleted. Instead of it, we have added a new option - :accepted_format
. The main principle of these two options is the same - validation of value passed to :caps
or :stream_format
action. While :caps
required a module name or specific tuple format as argument, :accepted_format
requires following types of terms:
Elixir pattern - eg. accepted_format: %My.Custom.Format{field: value} when value in [:some, :enumeration]
The value passed to :stream_format
action has to match the provided pattern. In this case, the requirement above would
be satisfied by eg. stream_format: %My.Custom.Format{field: :some}
Module name - eg. accepted_format: My.Custom.Format
This would be equal to the match on the struct of the passed module, in this case accepted_format: %My.Custom.Format{}
Call to any_of
function - you can pass as many arguments to it, as you want. Each argument should be an Elixir pattern
or a module name, eg. stream_format: any_of(My.Custom.Format, %My.Another.Custom.Format{field: :value})
If you use any_of
, the value passed to :stream_format
will have to match at least one of the passed
arguments. In this case, stream_format: %My.Custom.Format{frequency: 1}
would be ok, but
stream_format: %My.Another.Custom.Format{field: :not_allowed_value}
would fail
Option :accepted_format
is required. If you don't want to perform any check on stream_format
, you can always write accepted_format: _any
, but it is not suggested.
Checks on stream_format
will be performed on both, intput
and output
pads, just as caps
were checked in those places.
Membrane.Bin.spec_t
and Membrane.Pipeline.spec_t
actions no more accept Membrane.ParentSpec
structure.Membrane.ParentSpec
module, have been changed - till now on, the children structure needs to be defined with the use of child/3
, child/4
, get_child/2
and get_child/3
functions from the Membrane.ChildrenSpec
. (#458)Membrane.ParentSpec.link_bin_input/1
has been renamed to Membrane.ChildrenSpec.bin_input/1
(#458)Membrane.ParentSpec.link_bin_output/2
has been renamed to Membrane.ChildrenSpec.bin_output/2
(#458)Membrane.Time
moduleMembrane.Time.to_<unit name>/1
has been renamed to Membrane.Time.round_to_<unit name>/1
in order to indicate that the result will be rounded. (#435)def_options
macro:type
field from the def_options
keyword list has been removed, and the type specification of the option defined within the macro is determined by the :spec
field (#466)Membrane.Pipeline.start/3
and Membrane.Pipeline.start_link/3
now return {:ok, pipeline_supervisor, pipeline}
.(#432)Membrane.Testing
Membrane.Testing.Pipeline.start_link/1
has been changed to Membrane.Testing.Pipeline.start_link_supervised!/1
(#432)Membrane.Testing.Pipeline.options()
now have a single :structure
field allowing to specify the test pipeline's children structure, in place of the former :links
and :children
fields.handle_call/3
callback in the pipeline and areply
and :reply_to
actions. (#334)Membrane.Source
, Membrane.Filter
, Membrane.Endpoint
and Membrane.Sink
there is a list of all the callbacks available to be implemented for these modules.Membrane.Time.<plural unit name>/1
now accepts Ratio
structure as an argument. (#435)Membrane.Time.round_to_timebase/2
function has been added. (#435)Membrane.FilterAggregator
that allows running multiple filters sequentially within one process has been added (still experimental) (#355)Membrane.Testing.MockResourceGuard
has been added, to help write tests with the new way of handling resources. (#478)Published by varsill over 2 years ago
Published by varsill over 2 years ago
The changes that do not affect API:
{:error, ...}
monadsMembrane.Testing.Pipeline
with a custom module injected behaves in a desired way once Membrane.Testing.Pipeline.execute_actions/2
function is called (the custom module does not need to implement handle_other({:execute_actions, actions}, ...)
anymore)Full changelog: v0.10.0 ... v0.10.1
Published by varsill over 2 years ago
Membrane.ParentSpec{}
from handle_init/1
context
argument in Pipeline
and Bin
Membrane.Element.WithInputPads.def_input_pads/1
Membrane.Element.WithOutputPads.def_output_pads/1
Membrane.Log.*
Membrane.ParentSpec.link_bin_input/2
Membrane.ParentSpec.to_bin_output/2
Membrane.Time
: is_t/1
, system_time/0
, native_unit/1
, day/1
, hour/1
, minute/1
, second/1
, millisecond/1
, microsecond/1
, nanosecond/1
Membrane.Pipeline.{prepare, play, stop}
deprecated and adding :playback
action insteadMembrane.Pipeline.stop_and_terminate/2
deprecated and adding Membrane.Pipeline.terminate/2
insteadMembrane.RemoteControlled.Pipeline
- a basic implementation of a Membrane.Pipeline
thatMembrane.ElementError
exception if such a situation occurs #341
Membrane.Testing.Pipeline
API - deprecating the Membrane.Testing.Pipeline.Options
usage, allowing to use pipeline_keyword_list_t
as options in Membrane.Testing.Pipeline.start/1
and Membrane.Testing.Pipeline.start_link/1
Full changelog: v0.9.0 ... v0.10.0
Published by bblaszkow06 over 2 years ago
The Membrane Core can now handle the demands in filters for you! 🎉
To enable this, use demand_mode: :auto
in your pad's definition:
def_input_pad :input,
demand_mode: :auto,
caps: :any
This will make the Core send a demand automatically, no need to use :demand
action anymore.
By adding the same option to the output pad, the Core will also handle any incoming demands and demands on all input pads, so it is suitable for cases when a filter consumes data from all inputs uniformly.
See how automatic demands are used in funnel
:stopped
will resultFull Changelog: https://github.com/membraneframework/membrane_core/compare/v0.8.2...v0.9.0
Published by mickel8 almost 3 years ago
Release includes:
Published by andpodob almost 3 years ago
Release includes:
Membrane.Logger
metadata in the Membrane.ParentSpec
. All children spawned from that Membrane.ParentSpec
will receive specified metadata.Membrane.Buffer
structure explicitly. Timestamps should no longer live in Membrane.Buffer.metadata field #335.Published by mickel8 over 3 years ago
From v0.7.0 of membrane_core
it's possible to group elements and bins into crash groups.
Crash group is a logical entity that prevents the whole pipeline from crashing when one of its children crashes.
children = %{
{:endpoint_bin, endpoint_id} => %EndpointBin{
# ...
}
}
spec = %ParentSpec{children: children, crash_group: {endpoint_id, :temporary}}
In this case we create a new children - EndpointBin and we add it to crash group with id endpoint_id
. When EndpointBin crashes the whole pipeline will still be alive.
When some child in a crash group crashes the callback handle_crash_group_down/3
is called.
@impl true
def handle_crash_group_down(crash_group_id, ctx, state) do
Membrane.Logger.info("Crash group: #{inspect(crash_group_id)} is down.")
# do some stuff
end
At this moment Crash Groups are only useful for elements with dynamic pads.
In this version we also introduce an enhancement in linking elements.
Sometimes there is a situation you want to link or create an element only when some requirements are met.
To make it easier we introduce a new syntax that looks like below
encoding_specific_links =
case encoding do
# when encoding is :H264 we want to add one additional element -- H264 parser.
:H264 -> &to(&1, {:h264_parser, ssrc}, %Membrane.H264.FFmpeg.Parser{alignment: :nal})
# when encoding is :OPUS we just passes what we got
:OPUS -> & &1
end
links = [
link_bin_input(pad)
|> pipe_fun(encoding_specific_links)
|> to({:track_filter, track_id}, %Membrane.WebRTC.TrackFilter{enabled: track_enabled})
# ...
]
defp pipe_fun(term, fun), do: fun.(term)
From now Testing.Source sends a new type of caps - RemoteStream. This can break some of your already existing tests.
Published by mat-hek almost 4 years ago
This release includes several bug fixes:
Published by mat-hek about 4 years ago
context
argument #278Membrane.Event.{StartOfStream, EndOfStream}
events. Since v0.5.0 they're replaced with dedicated action and callbacks #280forward
action in pipelines and bins now accepts list #279Published by mat-hek about 4 years ago
Published by mat-hek over 4 years ago
Membrane Time
#248Published by mat-hek over 4 years ago
as_*
functions #223Published by bblaszkow06 over 4 years ago
Published by mat-hek almost 5 years ago
Although the main feature of this release is the introduction of bins, let's start with the refreshed syntax for links, as we'll need them for bins also. Until now, the way you declared links was the following:
links = %{
{:file_src, :output} => {:decoder, :input},
{:decoder, :output} => {:converter, :input},
{:converter, :output} => {:player, :input}
}
That was a bit too verbose and made trouble when linking a dynamic output pad multiple times, causing repeated keys in the map. The new way solves both problems:
links = [
link(:file_src) |> to(:decoder) |> to(:converter) |> to(:player)
]
If a pipeline is not linear, more elements can be added to the list:
links = [
link(:microphone) |> to(:tee) |> to(:player),
link(:tee) |> to(:file)
]
As :output
and :input
are default pad names, there is no need to provide them explicitly. If a custom name or pad options should be passed, via_out/2
and via_in/2
functions come to help.
links = [
link(:element1)
|> via_out(:custom_output, options: [key: :value])
|> via_in(:custom_input)
|> to(:element2)
]
Also, the way dynamic pads are referenced has been unified. Now, instead of passing a pad name and id to a link, full reference should be passed. As the existing format of references {:dynamic, name, id}
would be a bit cumbersome for that, it is now handled with Membrane.Pad.ref(name, id)
macro.
links = [
link(:file_src) |> via_in(Pad.ref(:input, 3)) |> to(:mixer)
]
See the docs for Membrane.ParentSpec
for more details.
Bins - entities that enable creating reusable, dynamically customisable groups of elements, are now available in Membrane. Bins can spawn their children like pipelines and have pads like elements, so they can be embedded within a pipeline (or another bin). Bins' pads proxy the stream between their siblings and children.
To use a bin, add it to children and link the same way as elements:
children = [
element1: Element1,
my_bin: My.Bin,
element2: Element2
]
links = [
link(:element1) |> to(:my_bin) |> to(:element2)
]
When linking children within a bin, you can connect them to bin's pad with use of link_bin_input/2
and to_bin_output/3
from Membrane.ParentSpec
.
links = [
link_bin_input() |> to(:child1) |> to(:child2) |> to_bin_output(:custom_output)
]
Bins can also have dynamic pads, and the new way of referencing them is intended to make this powerful blend straightforward to reason about.
@impl true
def handle_pad_added(Pad.ref(:input, _) = pad, _ctx, state) do
links = [link_bin_input(pad) |> to(:mixer)]
{{:ok, spec: %ParentSpec{links: links}}, state}
end
For more about bins check out the guide chapter about bins, see how to create and deal with bins in the documentation for Membrane.Bin
, or have a look at RTP bin or RTP demo.
Membrane.Pipeline.Spec
is now Membrane.ParentSpec
Membrane.Pad.ref(name, id)
macro instead of {:dynamic, name, id}
tupleMembrane.ParentSpec
pipeline_clock
became parent_clock
in all the callback contextsPublished by bblaszkow06 almost 5 years ago
This release fixes warning printed when pipeline is stopped via stop_and_terminate
function.
Published by bblaszkow06 about 5 years ago
start_timer
without explicitly passing a clock (#195)Published by bblaszkow06 about 5 years ago
The main feature of this release is synchronization utilities: clocks, timers and stream synchronization.
We added a new field in Membrane.Pipeline.Spec
- stream_sync
that accepts either atom :sinks
or a list of lists containing elements' names.
%Spec{stream_sync: [[:element1, :element2], [:element3, :element4]]}
%Spec{stream_sync: :sinks}
See this guide chapter for more info.
In addition, each element can declare its latency affecting how the synchronization works. Element declaring 100 ms of latency will be started (will receive start of stream event) 100 ms earlier than element without latency.
Latency can be declared by returning :latency
action from handle_init
callback.
@impl true
def handle_init(options) do
state = options |> Map.from_struct()
{{:ok, latency: 20 |> Membrane.Time.milliseconds()}, state}
end
Elements may provide a different source of time - like a hardware clock from sound card or elapsed time according to some library. This release introduces a def_clock
macro that can be used in elements exposing a clock.
The clock process spawned by the framework for such element will expect :membrane_clock_update
messages containing a number of milliseconds until the next update.
A detailed guide on how to expose custom clock from an element can be found here
Since this release you can use timers started and stopped by 2 new actions: :start_timer
and :stop_timer
.
@impl true
def handle_start_of_stream(:input, ctx, state) do
{{:ok, start_timer: {:demand_timer, 10}}, state}
end
Timers send ticks in intervals configured when a timer is started. The ticks can be handled by a new callback -
handle_tick/3
. The details can be found in guide
@impl true
def handle_tick(:demand_timer, _ctx, state) do
{{:ok, demand: :input}, state}
end
This Pipeline was created to reduce testing boilerplate and ease communication with its elements. It also provides a utility for informing the testing process about playback state changes and received notifications.
When you want a build Pipeline to test your elements you need three things:
When creating pipelines for tests the only essential part is the list of elements. In most cases during the tests, elements are linked in a way that the :output
pad is linked to the :input
pad of subsequent element. So we only need to pass a list of elements and links can be generated automatically
{:ok, pipeline} = Membrane.Testing.Pipeline.start_link(
%Membrane.Testing.Pipeline.Options {
elements: [
source: %Membrane.Testing.Source{},
tested_element: TestedElement,
sink: %Membrane.Testing.Sink{}
]
}
)
In addition to that this module contains utilities like message_child/3
and populate_links/1
You can also pass a custom pipeline module, by using :module
field of Membrane.Testing.Pipeline.Options struct. Every callback of the module will be executed before the callbacks implemented by Membrane.Testing.Pipeline. Initialization arguments can be passed via the :custom_args
field.
%Membrane.Testing.Pipeline.Options {
...
module: Your.Module,
custom_args: [:your_custom_args]
}
Membrane.Testing.Source
is an element that can output buffers from a data source passed as an option. It is especially useful when testing elements that network packets, e.g. RTP parser.
The data source can be either an enumerable:
%Membrane.Testing.Source{output: [0xA1, 0xB2, 0xC3, 0xD4]}
or a generator function:
initial_state = 1
generator_function = fn state, size ->
# generate actions that source will return from handle_demand/4
{actions, next_state}
end
%Membrane.Testing.Source{output: {initial_state, generator_function}}
This element will notify the pipeline about buffers, caps and events it receives and by default make demands automatically. Alternatively, you can set an appropriate option and Membrane.Testing.Sink
will no longer automatically make demands and will let you trigger demands by sending it a {:make_demand, size}
message.
In conjunction with Membrane.Testing.Assertions
it also allows you to assert on data it processed.
assert_sink_buffer(pipeline_pid, :sink ,%Membrane.Buffer{payload: 255})
To make element integration test possible we introduced two groups of assertions.
First works in concjunction with Membrane.Testing.Pipeline
:
assert_pipeline_notified(pipeline, element_name, notification_pattern, timeout)
assert_pipeline_playback_changed(pipeline, previous_state, current_state, timeout)
assert_pipeline_receive(pipeline, message_pattern, timeout)
refute_pipeline_receive(pipeline, message_pattern, timeout)
assert_start_of_stream(pipeline, element_name, pad, timeout)
assert_end_of_stream(pipeline, element_name, pad, timeout)
Second group works in conjunction with Membrane.Testing.Sink
.
assert_sink_buffer(pipeline, sink_name, pattern, timeout)
assert_sink_caps(pipeline, element_name, caps_pattern, timeout)
refute_sink_caps(pipeline, element_name, caps_pattern, timeout)
refute_sink_buffer(pipeline, sink_name, pattern, timeout)
assert_sink_event(pipeline, sink_name, event, timeout)
refute_sink_event(pipeline, sink_name, event, timeout)
For details, please read docs.
%Membrane.Event.StartOfStream{}
and %Membrane.Event.EndOfStream{}
will no longer be handled by handle_event/4
callback.
@impl true
def handle_event(pad, %Membrane.Event.StartOfStream{}, context, state) do
# Do important work
{:ok, state}
end
@impl true
def handle_event(pad, %Membrane.Event.EndOfStream{}, context, state) do
# Do important work
{:ok, state}
end
They will now be handled in their respective dedicated callbacks.
@impl true
def handle_start_of_stream(pad, context, state) do
# Do important work
{:ok, state}
end
@impl true
def handle_end_of_stream(pad, context, state) do
# Do important work
{:ok, state}
end
Published by bblaszkow06 over 5 years ago
To prepare for the next feature, we slightly changed the way pads are defined. Instead of defining them in groups with def_{in,out}put_pads/1
you should use def_{in,out}put_pad/2
for each pad.
So, this:
def_output_pads output: [mode: :push, caps: :any]
becomes this:
def_output_pad :output, mode: :push, caps: :any
The old way is now deprecated.
This small change results in nicer formatting of more complex definitions and prevents very deep list nesting, especially when defining...
From now on, not only elements can have options - but pads as well. Options for a pad are defined similarly to the element's options:
def_input_pad :input,
availability: :on_request,
demand_unit: :bytes,
caps: :any,
options: [
mute: [
type: :boolean,
spec: boolean() | nil,
default: nil,
description: """
Determines whether the pad will be muted from the start. If set to `nil` (default)
the default from the element options will be used.
"""
]
]
The values for pad options are passed while linking pads (as keyword under :pad
key). The former PullBuffer
(now renamed to InputBuffer
) options are passed under :buffer
key:
links = %{
# ...
{:file_src_b, :output} =>
{:mixer, :input, pad: [mute: true], buffer: [preferred_size: 264_600]},
# ...
}
When linking push output with pull input pad, the 'toilet' mode is now enabled automatically. It can still be configured when linking pads
links = %{
# ...
{:some_source, :output} =>
{:some_sink, :input, buffer: [warn_size: 264_600, fail_size: 529_200]},
# ...
}
Membrane.Time.system_time/0
is now deprecated in favour of Membrane.Time.os_time/0
and Membrane.Time.vm_time/0
that make it clear from which system the time is taken.
You no longer have to use functions from Membrane.Pipeline
module to start/play/stop the pipeline. Now every pipeline (that has use Membrane.Pipeline
line) gets automatically generated proxy functions:
start/2
, start_link/2
, play/1
, prepare/1
, stop/1
, stop_and_terminate/1
The docs generation for elements have been revised. Now description of available options and pads (along with their options) are placed in moduledoc (like in Membrane.Testing.Sink
)
Additionally, the docs for Membrane Core are properly grouped dramatically improving navigation