defmodule Membrane.RTP.Packet do @moduledoc """ Defines a struct describing an RTP packet and a way to parse it. Based on [RFC3550](https://tools.ietf.org/html/rfc3550#page-13) """ alias Membrane.RTP.{Header, Packet} @type t :: %__MODULE__{ header: Header.t(), payload: binary() } defstruct [ :header, :payload ] @type error_reason() :: :wrong_version | :packet_malformed @spec parse(binary()) :: {:ok, Packet.t()} | {:error, error_reason()} def parse(<>) when version != 2, do: {:error, :wrong_version} def parse(bytes) when byte_size(bytes) < 4 * 3, do: {:error, :packet_malformed} def parse( <> ) do {parsed_csrc, rest} = extract_csrcs(rest, cc) {extension_header, payload} = extract_extension_header(x, rest) padding? = extract_boolean(p) payload = strip_padding(payload, padding?) packet = %Packet{ header: %Header{ version: v, marker: extract_boolean(m), padding: padding?, extension_header: extract_boolean(x), csrc_count: cc, ssrc: ssrc, sequence_number: sequence_number, payload_type: payload_type, timestamp: timestamp, csrcs: parsed_csrc, extension_header_data: extension_header }, payload: payload } {:ok, packet} end defp extract_csrcs(data, count, acc \\ []) defp extract_csrcs(data, 0, acc), do: {acc, data} defp extract_csrcs(<>, count, acc), do: extract_csrcs(rest, count - 1, [csrc | acc]) defp extract_extension_header(is_header_present, data) defp extract_extension_header(0, data), do: {nil, data} defp extract_extension_header(1, binary_data) do <> = binary_data extension_data = %Header.Extension{ profile_specific: profile_specific, header_extension: header_ext } {extension_data, rest} end defp extract_boolean(read_value) defp extract_boolean(1), do: true defp extract_boolean(0), do: false def strip_padding(is_padding_present, payload) def strip_padding(payload, false), do: payload def strip_padding(payload, true) do padding_size = :binary.last(payload) payload_size = byte_size(payload) - padding_size <> = payload stripped_payload end end