lib/membrane/rtp/header_extension.ex

defmodule Membrane.RTP.Header.Extension do
  @moduledoc """
  Describes RTP Header Extension defined in [RFC3550](https://tools.ietf.org/html/rfc3550#section-5.3.1)

  ```
   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |      defined by profile       |           length              |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                        header extension                       |
  |                             ....                              |

  ```
  """
  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 pop(Buffer.t(), identifier_t()) :: {t() | nil, Buffer.t()}
  def pop(buffer, identifier) do
    extension = Enum.find(buffer.metadata.rtp.extensions, &(&1.identifier == identifier))

    if extension do
      buffer =
        Bunch.Struct.update_in(
          buffer,
          [:metadata, :rtp, :extensions],
          &delete(&1, identifier)
        )

      {extension, buffer}
    else
      {nil, buffer}
    end
  end

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

  defp delete(extensions, identifier) do
    Enum.reject(extensions, &(&1.identifier == identifier))
  end
end