Packages
membrane_rtp_plugin
0.12.2
0.31.5
0.31.4
0.31.3
0.31.2
0.31.1
0.31.0
0.30.0
0.29.1
0.29.0
0.28.0
0.27.1
0.27.0
0.26.0
0.25.0
0.24.1
0.24.0
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
0.19.1
0.19.0
0.18.0
0.17.1
0.17.0
0.16.0
0.15.0
0.15.0-rc.1
0.14.0
0.13.0
0.12.2
0.12.1
0.12.0
0.11.0
0.10.0
0.9.0
0.8.2
0.8.1
0.8.0
0.7.1-alpha.3
0.7.1-alpha.2
0.7.0-alpha.2
0.7.0-alpha.1
0.7.0-alpha
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0-alpha
Membrane Multimedia Framework plugin for RTP
Current section
Files
Jump to
Current section
Files
lib/membrane/rtcp/sdes_packet.ex
defmodule Membrane.RTCP.SdesPacket do
@moduledoc """
Parses Source Description (SDES) RTCP Packets defined in
[RFC3550](https://tools.ietf.org/html/rfc3550#section-6.5)
"""
@behaviour Membrane.RTCP.Packet
defmodule Chunk do
@moduledoc false
@type t :: %__MODULE__{
cname: String.t() | nil,
name: String.t() | nil,
email: String.t() | nil,
phone: String.t() | nil,
loc: String.t() | nil,
tool: String.t() | nil,
note: String.t() | nil,
priv: {String.t(), String.t()} | nil
}
defstruct [
:cname,
:name,
:email,
:phone,
:loc,
:tool,
:note,
:priv
]
end
defstruct chunks: []
@type t() :: %__MODULE__{
chunks: %{
required(Membrane.RTP.ssrc_t()) => Keyword.t()
}
}
@sdes_type_atom %{
1 => :cname,
2 => :name,
3 => :email,
4 => :phone,
5 => :loc,
6 => :tool,
7 => :note,
8 => :priv
}
@sdes_type_from_atom %{
:cname => 1,
:name => 2,
:email => 3,
:phone => 4,
:loc => 5,
:tool => 6,
:note => 7,
:priv => 8
}
@impl true
def decode(packet, ssrc_count) do
with {:ok, chunks} <- parse_chunks(packet, []),
true <- ssrc_count == length(chunks) do
chunks =
chunks |> Enum.into(%{}, fn {ssrc, items} -> {ssrc, struct(__MODULE__.Chunk, items)} end)
{:ok, %__MODULE__{chunks: chunks}}
else
{:error, _reason} = err -> err
false -> {:error, :invalid_ssrc_count}
end
end
defp parse_chunks(<<>>, acc), do: {:ok, acc}
defp parse_chunks(<<ssrc::32, rest::binary>>, acc) do
with {:ok, items, rest} <- parse_items(rest) do
parse_chunks(rest, [{ssrc, items} | acc])
end
end
defp parse_items(sdes_item, acc \\ [])
# null item denoting end of the list
defp parse_items(<<0::8, rest::binary>>, acc) do
# skip padding unitil next 32-bit boundary
to_skip = rest |> bit_size |> rem(32)
<<_skipped::size(to_skip), next_chunk::binary>> = rest
{:ok, acc, next_chunk}
end
# PRIV item
defp parse_items(<<8::8, length::8, content::binary-size(length), rest::binary>>, acc) do
<<prefix_len::8, prefix::binary-size(prefix_len), value>> = content
item = {@sdes_type_atom[8], {prefix, value}}
parse_items(rest, [item | acc])
end
defp parse_items(<<si_type::8, length::8, value::binary-size(length), rest::binary>>, acc)
when si_type in 0..7 do
item = {@sdes_type_atom[si_type], value}
parse_items(rest, [item | acc])
end
defp parse_items(<<_si_type::8, _payload::binary>>, _acc) do
{:error, :unknown_si_type}
end
@impl true
def encode(%__MODULE__{chunks: chunks}) do
chunk_list = chunks |> Enum.to_list()
body =
chunk_list
|> Enum.map_join(fn {ssrc, chunk} ->
<<ssrc::32, encode_chunk(chunk)::binary>>
end)
{body, length(chunk_list)}
end
@spec encode_chunk(chunk :: Chunk.t()) :: binary()
defp encode_chunk(chunk) do
body =
chunk
|> Map.from_struct()
|> Enum.map_join(fn
{_key, nil} ->
<<>>
{:priv, {prefix, value}} ->
prefix_len = byte_size(prefix)
total_len = 1 + prefix_len + byte_size(value)
<<8::8, total_len::8, prefix_len::8, prefix::binary(), value::binary()>>
{key, value} ->
<<@sdes_type_from_atom[key]::8, byte_size(value)::8, value::binary()>>
end)
pad_bits = 32 - (body |> bit_size() |> rem(32))
end_marker = <<0::size(pad_bits)>>
body <> end_marker
end
end