Parses RTP header extension elements inside an RFC 3550 extension block.
What problem this solves
WebRTC packs many RTP header-extension elements (audio level, TWCC,
Dependency Descriptor, RID / rtp-stream-id, etc.) into the RFC 3550
extension payload. SFU code needs to decode, rewrite, and re-encode those
elements without corrupting neighbors.
An RTP packet can carry extra metadata after the fixed 12-byte header (for
example, per-participant audio levels in a conference mixer). RFC 3550
defines an optional extension header; RFC 5285 and its successor RFC 8285
define how individual extension elements are packed into the extension
payload. WebRTC commonly uses the one-byte form (profile 0xBEDE).
This module decodes one-byte (0xBEDE) and two-byte (0x1000) element
layouts, encodes elements back into a padded payload, and exposes helpers
such as RFC 6464 audio level. Two-byte form is required when an element
exceeds 16 octets (one-byte max).
Typical flow: decode the RTP packet with XMediaLib.Rtp.decode/1, read
%XMediaLib.Rtp.Extension{} from :extension, then call
decode_elements/2 with the profile type.
Profiles
:one_byte/0xBEDE-- RFC 8285 pt.4.2:two_byte/0x1000-- RFC 8285 pt.4.3
RFCs
Summary
Functions
Reads RFC 6464 audio level from a decoded element list for local id.
Decodes extension elements for profile type (0xBEDE or 0x1000).
Decodes a one-byte (RFC 8285 pt.4.2) extension payload into {id, data} pairs.
Decodes a two-byte (RFC 8285 pt.4.3) extension payload into {id, data} pairs.
Encodes elements for profile type.
Encodes {id, data} pairs as a one-byte (RFC 8285) extension payload.
Encodes {id, data} pairs as a two-byte (RFC 8285) extension payload.
One-byte profile id (0xBEDE).
Chooses 0xBEDE when every element fits one-byte rules; otherwise 0x1000.
Reads the RID string from a decoded element list for local id.
SDP extmap URI for the RTP Stream Identifier (RID) header extension.
Two-byte profile id (0x1000).
Functions
@spec audio_level([{pos_integer(), binary()}], pos_integer()) :: {:ok, %{voice?: boolean(), level: 0..127}} | :error
Reads RFC 6464 audio level from a decoded element list for local id.
The first octet of the element data is V (voice activity, 1 bit) and
level (7 bits, 0 is loudest, 127 is silence).
Returns {:ok, %{voice?: boolean(), level: 0..127}} or :error when the id
is missing or the data is too short.
Examples
iex> elements = [{1, <<0x8A>>}, {3, <<0x01>>}]
iex> XMediaLib.Rtp.HeaderExtension.audio_level(elements, 1)
{:ok, %{voice?: true, level: 10}}
iex> XMediaLib.Rtp.HeaderExtension.audio_level([{2, <<0x8A>>}], 1)
:error
@spec decode_elements(non_neg_integer(), binary()) :: [{pos_integer(), binary()}]
Decodes extension elements for profile type (0xBEDE or 0x1000).
Unknown profiles return [].
@spec decode_one_byte_elements(binary()) :: [{pos_integer(), binary()}]
Decodes a one-byte (RFC 8285 pt.4.2) extension payload into {id, data} pairs.
Skips zero padding octets. Stops on truncated data or reserved local id 15.
Examples
iex> payload = <<1::4, 0::4, 0x8A, 0, 0>>
iex> XMediaLib.Rtp.HeaderExtension.decode_one_byte_elements(payload)
[{1, <<0x8A>>}]
iex> payload = <<1::4, 0::4, 0x05, 0, 2::4, 1::4, 0xAA, 0xBB>>
iex> XMediaLib.Rtp.HeaderExtension.decode_one_byte_elements(payload)
[{1, <<0x05>>}, {2, <<0xAA, 0xBB>>}]
@spec decode_two_byte_elements(binary()) :: [{pos_integer(), binary()}]
Decodes a two-byte (RFC 8285 pt.4.3) extension payload into {id, data} pairs.
Skips zero padding. Stops on truncated data. Id 0 is padding (skipped).
@spec encode_elements(non_neg_integer(), [{pos_integer(), binary()}]) :: binary()
Encodes elements for profile type.
For 0xBEDE, elements with data longer than 16 octets are dropped from the
one-byte encode (callers should upgrade profile via profile_for/1).
@spec encode_one_byte_elements([{pos_integer(), binary()}]) :: binary()
Encodes {id, data} pairs as a one-byte (RFC 8285) extension payload.
Pads to a multiple of 4 octets. Ids must be 1..14; empty list yields four zero padding octets.
@spec encode_two_byte_elements([{pos_integer(), binary()}]) :: binary()
Encodes {id, data} pairs as a two-byte (RFC 8285) extension payload.
Pads to a multiple of 4 octets. Ids must be 1..255; data length 0..255.
@spec one_byte_profile() :: 48862
One-byte profile id (0xBEDE).
@spec profile_for([{pos_integer(), binary()}]) :: 48862 | 4096
Chooses 0xBEDE when every element fits one-byte rules; otherwise 0x1000.
@spec rtp_stream_id([{pos_integer(), binary()}], pos_integer()) :: {:ok, binary()} | :error
Reads the RID string from a decoded element list for local id.
WebRTC packs the Restriction Identifier as UTF-8 / ASCII in the
sdes:rtp-stream-id extension payload.
Returns {:ok, rid} or :error when missing or empty.
Examples
iex> XMediaLib.Rtp.HeaderExtension.rtp_stream_id([{4, "q"}], 4)
{:ok, "q"}
iex> XMediaLib.Rtp.HeaderExtension.rtp_stream_id([{4, "q"}], 5)
:error
@spec rtp_stream_id_uri() :: String.t()
SDP extmap URI for the RTP Stream Identifier (RID) header extension.
@spec two_byte_profile() :: 4096
Two-byte profile id (0x1000).