XMediaLib.Rtp.HeaderExtension (xmedialib v0.3.0)

Copy Markdown View Source

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

  • RFC 3550 - RTP header extension container (profile-specific extension)
  • RFC 5285 - one-byte and two-byte header extensions (obsoleted by RFC 8285)
  • RFC 8285 - current header extension specification
  • RFC 6464 - Client-to-Mixer Audio Level Indication

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

audio_level(elements, id)

@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

decode_elements(type, payload)

@spec decode_elements(non_neg_integer(), binary()) :: [{pos_integer(), binary()}]

Decodes extension elements for profile type (0xBEDE or 0x1000).

Unknown profiles return [].

decode_one_byte_elements(payload)

@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>>}]

decode_two_byte_elements(payload)

@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).

encode_elements(type, elements)

@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).

encode_one_byte_elements(elements)

@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.

encode_two_byte_elements(elements)

@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.

one_byte_profile()

@spec one_byte_profile() :: 48862

One-byte profile id (0xBEDE).

profile_for(elements)

@spec profile_for([{pos_integer(), binary()}]) :: 48862 | 4096

Chooses 0xBEDE when every element fits one-byte rules; otherwise 0x1000.

rtp_stream_id(elements, id)

@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

rtp_stream_id_uri()

@spec rtp_stream_id_uri() :: String.t()

SDP extmap URI for the RTP Stream Identifier (RID) header extension.

two_byte_profile()

@spec two_byte_profile() :: 4096

Two-byte profile id (0x1000).