lib/membrane/rtcp/bye_packet.ex

defmodule Membrane.RTCP.ByePacket do
  @moduledoc """
  Parses and constructs RTCP BYE packets defined in
  [RFC3550](https://tools.ietf.org/html/rfc3550#section-6.6)
  """

  @behaviour Membrane.RTCP.Packet

  @type t :: %__MODULE__{
          ssrcs: [non_neg_integer()],
          reason: String.t() | nil
        }

  defstruct [:ssrcs, :reason]

  @impl true
  def decode(packet, count) do
    ssrcs_size = count * 4
    <<ssrcs::binary-size(ssrcs_size), reason::binary>> = packet

    ssrcs =
      ssrcs
      |> Bunch.Binary.chunk_every(4)
      |> Enum.map(&:binary.decode_unsigned(&1))

    result = %__MODULE__{ssrcs: ssrcs, reason: make_reason(reason)}
    {:ok, result}
  end

  @impl true
  def encode(%{ssrcs: ssrcs, reason: reason}) do
    count = ssrcs |> length()
    ssrcs = ssrcs |> Enum.map_join(&<<&1::32>>)

    reason =
      case reason do
        nil ->
          <<>>

        other ->
          length = String.length(other)
          <<length::8, other::binary>>
      end

    {ssrcs <> reason, count}
  end

  defp make_reason(<<>>), do: nil
  defp make_reason(<<_length::8, reason::binary>>), do: reason
end