Releases: membraneframework/membrane_core
v0.4.1
v0.4.0 - Synchronization and more
Membrane Framework 0.4 release notes
Synchronization
The main feature of this release is synchronization utilities: clocks, timers and stream synchronization.
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
Clocks
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
Timers
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
Testing API
Testing Pipeline
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:
- Pipeline Module
- List of elements
- Links between those elements
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]
}
Testing Source
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}}
Testing Sink
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})
Assertions
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.
Deprecations
Deprecation of Start and End of stream events in favor of custom callback
%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
v0.3.0
New in release v0.3.0
New way to define pads
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...
Pad options
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]},
# ...
}
Push to pull connection
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]},
# ...
}
System time - but which system?
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.
Proxy functions for Membrane.Pipeline
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
Documentation
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
v0.2.2
v0.2.1
Features
- Elements and utilities for testing elements. See the documentation of
Membrane.Testing.*
modules (#125) - Interpolation and paragraphs are now handled properly in descriptions provided via
def_options
macro (#133) Membrane.Time
formatting utilities (#134)
Fixes
v0.2.0
Enhancements:
- Abstraction layer over NIFs - Unifex
- Support for payloads stored in shared memory - Shmex
- First video processing elements - H.264 codec, Raw video parser
- Element API improvements
- Demand handling improvements
- Better capabilities verification
- Significant refactor of Core internal structure
- Docs and typespecs improvements
- Integration tests
Breaking changes:
- Change of pad directions names:
sink
->input
,source
->output
Element and pipeline
- New state change callbacks names, currently, they have form of
handle_{old_playback_state}_to_{new_playback_state}
, e.g.handle_prepared_to_playing
Element
- Unified behaviour of demands (see #66 and demands section in guide)
- No automatic redemands - they have to be triggered explicitly with
:redemand
action - Context argument present in
handle_other
and all playback state change callbacks - Callback contexts refactor, now they contain caps and other parameters of all pads and current playback state
- Refactor of pads declaration
v0.1.1
- Fix compatibility with Elixir 1.7
- Fix Github links in docs