Packages
membrane_rtmp_plugin
0.18.0
0.29.5
0.29.4
0.29.3
0.29.2
0.29.1
0.29.0
0.28.1
0.28.0
0.27.3
0.27.2
0.27.0
0.26.0
0.25.0
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.2
0.20.1
0.20.0
0.19.3
0.19.2
retired
0.19.1
0.19.0
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.15.0
0.14.0
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.1
0.6.0
0.5.0
0.4.1
0.4.0
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
RTMP Plugin for Membrane Multimedia Framework
Current section
Files
Jump to
Current section
Files
lib/membrane_rtmp_plugin/rtmp/source/handshake/step.ex
defmodule Membrane.RTMP.Handshake.Step do
@moduledoc false
# Describes steps in the process of RTMP handshake
@enforce_keys [:data, :type]
defstruct @enforce_keys
@typedoc """
RTMP handshake types.
The handshake flow between client and server looks as follows:
+-------------+ +-------------+
| Client | TCP/IP Network | Server |
+-------------+ | +-------------+
| | |
Uninitialized | Uninitialized
| C0 | |
|------------------->| C0 |
| |-------------------->|
| C1 | |
|------------------->| S0 |
| |<--------------------|
| | S1 |
Version sent |<--------------------|
| S0 | |
|<-------------------| |
| S1 | |
|<-------------------| Version sent
| | C1 |
| |-------------------->|
| C2 | |
|------------------->| S2 |
| |<--------------------|
Ack sent | Ack Sent
| S2 | |
|<-------------------| |
| | C2 |
| |-------------------->|
Handshake Done | Handshake Done
| | |
Where `C0` and `S0` are RTMP protocol version (set to 0x03).
Both sides exchange random chunks of 1536 bytes and the other side is supposed to
respond with those bytes remaining unchanged.
In case of `S1` and `S2`, the latter is supposed to be equal to `C1` while
the client has to respond by sending `C2` with the `S1` as the value.
"""
@type handshake_type_t :: :c0_c1 | :s0_s1_s2 | :c2
@type t :: %__MODULE__{
data: binary(),
type: handshake_type_t()
}
@rtmp_version 0x03
@handshake_size 1536
@s1_s2_size 2 * @handshake_size
defmacrop invalid_step_error(type) do
quote do
{:error, {:invalid_handshake_step, unquote(type)}}
end
end
@doc """
Serializes the step.
"""
@spec serialize(t()) :: binary()
def serialize(%__MODULE__{type: type, data: data}) when type in [:c0_c1, :s0_s1_s2] do
<<@rtmp_version, data::binary>>
end
def serialize(%__MODULE__{data: data}), do: data
@doc """
Deserializes the handshake step given the type.
"""
@spec deserialize(handshake_type_t(), binary()) ::
{:ok, t()} | {:error, :invalid_handshake_step}
def deserialize(:c0_c1 = type, <<0x03, data::binary-size(@handshake_size)>>) do
{:ok, %__MODULE__{type: type, data: data}}
end
def deserialize(:s0_s1_s2 = type, <<0x03, data::binary-size(@s1_s2_size)>>) do
{:ok, %__MODULE__{type: type, data: data}}
end
def deserialize(:c2 = type, <<data::binary-size(@handshake_size)>>) do
{:ok, %__MODULE__{type: type, data: data}}
end
def deserialize(_type, _data), do: {:error, :invalid_handshake_step}
@doc """
Verifies if the following handshake step matches the previous one.
C1 should have the same value as S2 and C2 be the same as S1.
"""
@spec verify_next_step(t() | nil, t()) ::
:ok | {:error, {:invalid_handshake_step, handshake_type_t()}}
def verify_next_step(previous_step, next_step)
def verify_next_step(nil, %__MODULE__{type: :c0_c1}), do: :ok
def verify_next_step(%__MODULE__{type: :c0_c1, data: c1}, %__MODULE__{
type: :s0_s1_s2,
data: s1_s2
}) do
<<_s1::binary-size(@handshake_size), s2::binary-size(@handshake_size)>> = s1_s2
if s2 == c1 do
:ok
else
invalid_step_error(:s0_s1_s2)
end
end
def verify_next_step(%__MODULE__{type: :s0_s1_s2, data: s1_s2}, %__MODULE__{type: :c2, data: c2}) do
<<s1::binary-size(@handshake_size), _s2::binary>> = s1_s2
if c2 == s1 do
:ok
else
invalid_step_error(:c2)
end
end
@doc """
Returns epoch timestamp of the connection.
"""
@spec epoch(t()) :: non_neg_integer()
def epoch(%__MODULE__{data: <<epoch::32, _rest::binary>>}), do: epoch
end