Packages
membrane_rtp_plugin
0.5.0
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/rtp/packet.ex
defmodule Membrane.RTP.Packet do
@moduledoc """
Defines a struct describing an RTP packet and a way to parse and serialize it.
Based on [RFC3550](https://tools.ietf.org/html/rfc3550#page-13)
"""
alias Membrane.RTP.{Header, Utils}
@type t :: %__MODULE__{
header: Header.t(),
payload: binary()
}
@enforce_keys [:header, :payload]
defstruct @enforce_keys
@spec serialize(t, align_to: pos_integer()) :: binary
def serialize(%__MODULE__{} = packet, [align_to: align_to] \\ [align_to: 1]) do
%__MODULE__{header: header, payload: payload} = packet
%Header{version: 2} = header
has_padding = 0
has_extension = if header.extension, do: 1, else: 0
marker = if header.marker, do: 1, else: 0
csrcs = Enum.map_join(header.csrcs, &<<&1::32>>)
serialized =
<<header.version::2, has_padding::1, has_extension::1, length(header.csrcs)::4, marker::1,
header.payload_type::7, header.sequence_number::16, header.timestamp::32, header.ssrc::32,
csrcs::binary, serialize_header_extension(header.extension)::binary, payload::binary>>
case Utils.align(serialized, align_to) do
{serialized, 0} ->
serialized
{serialized, _padding} ->
<<pre::2, _has_padding::1, post::bitstring>> = serialized
<<pre::2, 1::1, post::bitstring>>
end
end
defp serialize_header_extension(nil) do
<<>>
end
defp serialize_header_extension(%Header.Extension{data: data} = extension)
when byte_size(data) |> rem(4) == 0 do
<<extension.profile_specific::16, byte_size(data) |> div(4)::16, data::binary>>
end
@spec parse(binary()) :: {:ok, t()} | {:error, :wrong_version | :malformed_packet}
def parse(<<version::2, _::bitstring>>) when version != 2,
do: {:error, :wrong_version}
def parse(
<<version::2, has_padding::1, has_extension::1, csrcs_cnt::4, marker::1, payload_type::7,
sequence_number::16, timestamp::32, ssrc::32, csrcs::binary-size(csrcs_cnt)-unit(32),
rest::binary>>
) do
with {:ok, {extension, payload}} <- parse_header_extension(rest, has_extension == 1),
{:ok, {payload, _padding}} = Utils.strip_padding(payload, has_padding == 1) do
header = %Header{
version: version,
marker: marker == 1,
ssrc: ssrc,
sequence_number: sequence_number,
payload_type: payload_type,
timestamp: timestamp,
csrcs: for(<<csrc::32 <- csrcs>>, do: csrc),
extension: extension
}
{:ok, %__MODULE__{header: header, payload: payload}}
else
:error -> {:error, :malformed_packet}
end
end
def parse(_binary), do: {:error, :malformed_packet}
defp parse_header_extension(binary, header_present?)
defp parse_header_extension(binary, false), do: {:ok, {nil, binary}}
defp parse_header_extension(
<<profile_specific::16, data_len::16, data::binary-size(data_len)-unit(32),
rest::binary>>,
true
) do
extension = %Header.Extension{profile_specific: profile_specific, data: data}
{:ok, {extension, rest}}
end
defp parse_header_extension(_binary, true), do: :error
end