Packages
membrane_core
0.8.0
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/timer_controller.ex
defmodule Membrane.Core.TimerController do
@moduledoc false
use Bunch
alias Membrane.Clock
alias Membrane.Core.{CallbackHandler, Component, Timer}
require Membrane.Core.Component
require Membrane.Element.CallbackContext.Tick
@spec start_timer(Timer.id_t(), Timer.interval_t(), Clock.t(), Component.state_t()) ::
{:ok, Component.state_t()}
| {{:error, {:timer_already_exists, id: Timer.id_t()}}, Component.state_t()}
def start_timer(id, interval, clock, state) do
if state.synchronization.timers |> Map.has_key?(id) do
{{:error, {:timer_already_exists, id: id}}, state}
else
clock |> Clock.subscribe()
timer = Timer.start(id, interval, clock)
state |> Bunch.Access.put_in([:synchronization, :timers, id], timer) ~> {:ok, &1}
end
end
@spec timer_interval(Timer.id_t(), Timer.interval_t(), Component.state_t()) ::
{:ok, Component.state_t()}
| {{:error, {:unknown_timer, id: Timer.id_t()}}, Component.state_t()}
def timer_interval(id, interval, state) do
with {:ok, timer} <- state.synchronization.timers |> Map.fetch(id) do
Bunch.Access.put_in(
state,
[:synchronization, :timers, id],
Timer.set_interval(timer, interval)
)
~> {:ok, &1}
else
:error -> {{:error, {:unknown_timer, id}}, state}
end
end
@spec stop_timer(Timer.id_t(), Component.state_t()) ::
{:ok, Component.state_t()}
| {{:error, {:unknown_timer, id: Timer.id_t()}}, Component.state_t()}
def stop_timer(id, state) do
{timer, state} = state |> Bunch.Access.pop_in([:synchronization, :timers, id])
if timer |> is_nil do
{{:error, {:unknown_timer, id}}, state}
else
:ok = timer |> Timer.stop()
timer.clock |> Clock.unsubscribe()
{:ok, state}
end
end
@spec handle_tick(Timer.id_t(), Component.state_t()) ::
{:ok, Component.state_t()} | {{:error, any}, Component.state_t()}
def handle_tick(timer_id, state) do
context = Component.callback_context_generator(:any, Tick, state)
# the first clause checks if the timer wasn't removed before receiving this tick
withl present?: true <- Map.has_key?(state.synchronization.timers, timer_id),
callback:
{:ok, state} <-
CallbackHandler.exec_and_handle_callback(
:handle_tick,
Component.action_handler(state),
%{context: context},
[timer_id],
state
),
# in case the timer was removed in handle_tick
present?: true <- Map.has_key?(state.synchronization.timers, timer_id) do
state
|> Bunch.Access.update_in([:synchronization, :timers, timer_id], &Timer.tick/1)
~> {:ok, &1}
else
present?: false -> {:ok, state}
callback: {{:error, _reason}, _state} = err -> err
end
end
@spec handle_clock_update(Timer.id_t(), Clock.ratio_t(), Component.state_t()) ::
{:ok, Component.state_t()}
def handle_clock_update(clock, ratio, state) do
state
|> update_in(
[:synchronization, :timers],
&Bunch.Map.map_values(&1, fn
%Timer{clock: ^clock} = timer -> timer |> Timer.update_ratio(ratio)
timer -> timer
end)
)
~> {:ok, &1}
end
end