Packages
membrane_rtmp_plugin
0.27.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/header.ex
defmodule Membrane.RTMP.Header do
@moduledoc false
@enforce_keys [:chunk_stream_id, :type_id]
defstruct @enforce_keys ++
[
body_size: 0,
timestamp: 0,
timestamp_delta: 0,
extended_timestamp?: false,
stream_id: 0
]
@typedoc """
RTMP header structure.
Fields:
* `chunk_stream_id` - chunk stream identifier that the following packet body belongs to
* `timestmap` - chunk timestamp, equals 0 when the header is a part of non-media message
* `body_size` - the size in bytes of the following body payload
* `type_id` - the type of the body payload, for more details please refer to the RTMP docs
* `stream_id` - stream identifier that the message belongs to
"""
@type t :: %__MODULE__{
chunk_stream_id: integer(),
timestamp: integer(),
body_size: integer(),
type_id: integer(),
stream_id: integer()
}
defmacro type(:set_chunk_size), do: 0x01
defmacro type(:acknowledgement), do: 0x03
defmacro type(:user_control_message), do: 0x04
defmacro type(:window_acknowledgement_size), do: 0x05
defmacro type(:set_peer_bandwidth), do: 0x06
defmacro type(:audio_message), do: 0x08
defmacro type(:video_message), do: 0x09
defmacro type(:amf_data), do: 0x12
defmacro type(:amf_command), do: 0x14
@header_type_0 <<0x0::2>>
@header_type_1 <<0x1::2>>
@header_type_2 <<0x2::2>>
@header_type_3 <<0x3::2>>
@extended_timestamp_marker 0xFFFFFF
@spec new(Keyword.t()) :: t()
def new(opts) do
struct!(__MODULE__, opts)
end
@doc """
Deserializes given binary into an RTMP header structure.
RTMP headers can be self contained or may depend on preceding headers.
It depends on the first 2 bits of the header:
* `0b00` - current header is self contained and contains all the header information, see `t:t/0`
* `0b01` - current header derives the `stream_id` from the previous header
* `0b10` - same as above plus derives `type_id` and `body_size`
* `0b11` - all values are derived from the previous header with the same `chunk_stream_id`
"""
@spec deserialize(binary(), t() | nil) :: {t(), rest :: binary()} | {:error, :need_more_data}
def deserialize(binary, previous_headers \\ nil)
# only the deserialization of the 0b00 type can have `nil` previous header
def deserialize(
<<@header_type_0::bitstring, chunk_stream_id::6, timestamp::24, body_size::24, type_id::8,
stream_id::little-integer-size(32), rest::binary>>,
_previous_headers
) do
with {timestamp, extended_timestamp?, rest} <- extract_timestamp(rest, timestamp) do
header = %__MODULE__{
chunk_stream_id: chunk_stream_id,
timestamp: timestamp,
extended_timestamp?: extended_timestamp?,
body_size: body_size,
type_id: type_id,
stream_id: stream_id
}
{header, rest}
end
end
def deserialize(
<<@header_type_1::bitstring, chunk_stream_id::6, timestamp_delta::24, body_size::24,
type_id::8, rest::binary>>,
previous_headers
) do
with {timestamp_delta, extended_timestamp?, rest} <- extract_timestamp(rest, timestamp_delta) do
header = %__MODULE__{
chunk_stream_id: chunk_stream_id,
timestamp: previous_headers[chunk_stream_id].timestamp + timestamp_delta,
timestamp_delta: timestamp_delta,
extended_timestamp?: extended_timestamp?,
body_size: body_size,
type_id: type_id,
stream_id: previous_headers[chunk_stream_id].stream_id
}
{header, rest}
end
end
def deserialize(
<<@header_type_2::bitstring, chunk_stream_id::6, timestamp_delta::24, rest::binary>>,
previous_headers
) do
with {timestamp_delta, extended_timestamp?, rest} <- extract_timestamp(rest, timestamp_delta) do
header = %__MODULE__{
chunk_stream_id: chunk_stream_id,
timestamp: previous_headers[chunk_stream_id].timestamp + timestamp_delta,
timestamp_delta: timestamp_delta,
extended_timestamp?: extended_timestamp?,
body_size: previous_headers[chunk_stream_id].body_size,
type_id: previous_headers[chunk_stream_id].type_id,
stream_id: previous_headers[chunk_stream_id].stream_id
}
{header, rest}
end
end
def deserialize(
<<@header_type_3::bitstring, chunk_stream_id::6, rest::binary>>,
previous_headers
) do
previous_header = previous_headers[chunk_stream_id]
if previous_header.extended_timestamp? do
with {timestamp_delta, _extended_timestamp?, rest} <-
extract_timestamp(rest, @extended_timestamp_marker) do
header = %__MODULE__{
previous_header
| timestamp: previous_header.timestamp + timestamp_delta,
timestamp_delta: timestamp_delta
}
{header, rest}
end
else
header = %__MODULE__{
previous_header
| timestamp: previous_header.timestamp + previous_header.timestamp_delta
}
{header, rest}
end
end
def deserialize(
<<@header_type_0::bitstring, _chunk_stream_id::6, _rest::binary>>,
_prev_header
),
do: {:error, :need_more_data}
def deserialize(
<<@header_type_1::bitstring, _chunk_stream_id::6, _rest::binary>>,
_prev_header
),
do: {:error, :need_more_data}
def deserialize(
<<@header_type_2::bitstring, _chunk_stream_id::6, _rest::binary>>,
_prev_header
),
do: {:error, :need_more_data}
@spec serialize(t()) :: binary()
def serialize(%__MODULE__{} = header) do
%{
chunk_stream_id: chunk_stream_id,
timestamp: timestamp,
body_size: body_size,
type_id: type_id,
stream_id: stream_id
} = header
<<@header_type_0::bitstring, chunk_stream_id::6, timestamp::24, body_size::24, type_id::8,
stream_id::little-integer-size(32)>>
end
defp extract_timestamp(<<timestamp::32, rest::binary>>, @extended_timestamp_marker),
do: {timestamp, true, rest}
defp extract_timestamp(_rest, @extended_timestamp_marker),
do: {:error, :need_more_data}
defp extract_timestamp(rest, timestamp),
do: {timestamp, false, rest}
end