lib/membrane/rtp/header_extension.ex

defmodule Membrane.RTP.Header.Extension do
  @moduledoc """
  Describes RTP Header Extension defined in [RFC8285](https://datatracker.ietf.org/doc/html/rfc8285)
  and provides common functions for interacting with extensions placed in buffers.

  ```
   0                   1                   2
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 ...
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
  |  ID   |  len  |     data (len+1 bytes)   ...
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
  ```
  """
  alias Membrane.Buffer
  alias Membrane.RTP.SessionBin

  @enforce_keys [:identifier, :data]
  defstruct @enforce_keys

  @type identifier_t :: 1..14 | SessionBin.rtp_extension_name_t()

  @type t :: %__MODULE__{
          identifier: identifier_t(),
          data: binary()
        }

  @spec find(Buffer.t(), identifier_t()) :: t() | nil
  def find(%Buffer{metadata: %{rtp: %{extensions: extensions}}}, identifier) do
    Enum.find(extensions, &(&1.identifier == identifier))
  end

  @spec put(Buffer.t(), t()) :: Buffer.t()
  def put(buffer, extension) do
    Bunch.Struct.update_in(buffer, [:metadata, :rtp, :extensions], &[extension | &1])
  end

  @spec delete(Buffer.t(), identifier_t()) :: Buffer.t()
  def delete(buffer, identifier) do
    Bunch.Struct.update_in(
      buffer,
      [:metadata, :rtp, :extensions],
      &Enum.reject(&1, fn extension -> extension.identifier == identifier end)
    )
  end

  @spec pop(Buffer.t(), identifier_t()) :: {t() | nil, Buffer.t()}
  def pop(buffer, identifier) do
    case find(buffer, identifier) do
      nil -> {nil, buffer}
      extension -> {extension, delete(buffer, identifier)}
    end
  end
end