Packages
membrane_core
1.2.7
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.7
1.2.6
1.2.5
retired
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc1
1.1.2
1.1.1
1.1.0
1.1.0-rc1
1.1.0-rc0
1.0.1
1.0.0
1.0.0-rc1
1.0.0-rc0
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
retired
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.2
0.10.1
0.10.0
0.9.0
0.8.2
0.8.1
0.8.0
0.7.0
0.6.1
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
Membrane Multimedia Framework (Core)
Current section
Files
Jump to
Current section
Files
lib/membrane/core/callback_handler.ex
defmodule Membrane.Core.CallbackHandler do
@moduledoc false
# Behaviour for module that delegates its job to the other module via callbacks.
# It delivers implementation of executing callbacks and results parsing their
# results.
use Bunch
alias Membrane.CallbackError
require Membrane.Logger
require Membrane.Core.Telemetry, as: Telemetry
@type state :: %{
:module => module,
:internal_state => internal_state,
optional(atom) => any
}
@type internal_state :: any
@type callback_return(action, internal_state) ::
{[action], internal_state}
@type callback_return :: callback_return(any, any)
@type handler_params :: map
@callback handle_action(action :: any, callback :: atom, handler_params, state) :: state
@callback transform_actions(actions :: list, callback :: atom, handler_params, state) ::
{actions :: list, state}
@callback handle_end_of_actions(state) :: state
defmacro __using__(_args) do
quote location: :keep do
alias unquote(__MODULE__)
@behaviour unquote(__MODULE__)
@impl unquote(__MODULE__)
def transform_actions(actions, _callback, _handler_params, state) do
{actions, state}
end
@impl unquote(__MODULE__)
def handle_end_of_actions(state) do
state
end
defoverridable unquote(__MODULE__)
end
end
@spec exec_and_handle_callback(
callback :: atom,
module,
handler_params,
args :: list,
state
) :: state
def exec_and_handle_callback(
callback,
handler_module,
handler_params,
args,
state
)
when is_map(handler_params) do
result = exec_callback(callback, args, handler_params, state)
handle_callback_result(result, callback, handler_module, handler_params, state)
end
@spec exec_and_handle_split_callback(
callback :: atom,
original_callback :: atom,
module,
handler_params,
args_list :: list,
state
) :: state
def exec_and_handle_split_callback(
callback,
original_callback,
handler_module,
handler_params,
args_list,
state
)
def exec_and_handle_split_callback(
callback,
original_callback,
handler_module,
%{split_continuation_arbiter: split_continuation_arbiter} = handler_params,
args_list,
state
) do
Enum.reduce_while(args_list, state, fn args, state ->
if split_continuation_arbiter.(state) do
state =
callback
|> exec_callback(args, handler_params, state)
|> handle_callback_result(original_callback, handler_module, handler_params, state)
{:cont, state}
else
{:halt, state}
end
end)
end
def exec_and_handle_split_callback(
callback,
original_callback,
handler_module,
%{} = handler_params,
args_list,
state
) do
Enum.reduce(args_list, state, fn args, state ->
callback
|> exec_callback(args, handler_params, state)
|> handle_callback_result(original_callback, handler_module, handler_params, state)
end)
end
@spec exec_callback(callback :: atom, args :: list, handler_params, state) ::
{list, internal_state} | no_return()
defp exec_callback(
callback,
args,
%{context: context_fun},
%{module: module, internal_state: internal_state} = state
) do
context = context_fun.(state)
args = args ++ [context, internal_state]
try do
fn ->
apply(module, callback, args)
|> validate_callback_result!(module, callback)
end
|> Telemetry.track_callback_handler(callback, args, state, context)
rescue
e in UndefinedFunctionError ->
_ignored =
with %{module: ^module, function: ^callback, arity: arity} <- e do
reraise CallbackError,
[
kind: :not_implemented,
callback: {module, callback},
arity: arity,
args: args
],
__STACKTRACE__
end
reraise e, __STACKTRACE__
end
end
defp validate_callback_result!({actions, _state} = callback_result, _module, _cb)
when is_list(actions) do
callback_result
end
defp validate_callback_result!(callback_result, module, callback) do
raise CallbackError,
kind: :bad_return,
callback: {module, callback},
value: callback_result
end
@spec handle_callback_result(
{actions :: Keyword.t(), internal_state},
callback :: atom,
module,
handler_params,
state
) :: state
defp handle_callback_result(cb_result, callback, handler_module, handler_params, state) do
{actions, new_internal_state} = cb_result
state = %{state | internal_state: new_internal_state}
{actions, state} =
try do
handler_module.transform_actions(actions, callback, handler_params, state)
rescue
e ->
Membrane.Logger.error("""
Error handling actions returned by callback #{inspect(state.module)}.#{callback}
""")
reraise e, __STACKTRACE__
end
state =
Enum.reduce(actions, state, fn action, state ->
try do
handler_module.handle_action(action, callback, handler_params, state)
rescue
e ->
Membrane.Logger.error("""
Error handling action returned by callback #{inspect(state.module)}.#{callback}.
Action: #{inspect(action, pretty: true)}
""")
reraise e, __STACKTRACE__
end
end)
handler_module.handle_end_of_actions(state)
end
end