lib/membrane/rtcp/transport_feedback_packet.ex

defmodule Membrane.RTCP.TransportFeedbackPacket do
  @moduledoc """
  Abstraction and generic encoding/decoding functionality for
  [RTCP transport layer feedback packets](https://datatracker.ietf.org/doc/html/rfc4585#section-6.1).
  """

  @behaviour Membrane.RTCP.Packet

  alias Membrane.RTP

  @enforce_keys [:sender_ssrc, :media_ssrc, :payload]
  defstruct @enforce_keys

  @type t :: %__MODULE__{
          sender_ssrc: RTP.ssrc_t(),
          media_ssrc: RTP.ssrc_t(),
          payload: struct
        }

  @callback decode(binary()) :: {:ok, struct()} | {:error, any()}

  @callback encode(struct()) :: binary()

  @packet_type_payload BiMap.new(%{
                         15 => __MODULE__.TWCC
                       })

  @impl true
  def decode(<<sender_ssrc::32, media_ssrc::32, payload::binary>>, packet_type) do
    with {:ok, module} <- BiMap.fetch(@packet_type_payload, packet_type),
         {:ok, payload} <- module.decode(payload) do
      {:ok, %__MODULE__{sender_ssrc: sender_ssrc, media_ssrc: media_ssrc, payload: payload}}
    else
      :error -> {:error, {:unknown_feedback_packet_type, packet_type}}
      {:error, reason} -> {:error, reason}
    end
  end

  @impl true
  def decode(_binary, _packet_type) do
    {:error, :malformed_packet}
  end

  @impl true
  def encode(%__MODULE__{} = packet) do
    %module{} = packet.payload
    packet_type = BiMap.fetch_key!(@packet_type_payload, module)

    {<<packet.sender_ssrc::32, packet.media_ssrc::32, module.encode(packet.payload)::binary>>,
     packet_type}
  end
end