defmodule Membrane.RTCP.SenderReportPacket do @moduledoc """ Parses and constructs RTCP Sender Report defined in [RFC3550](https://tools.ietf.org/html/rfc3550#section-6.4.1) """ @behaviour Membrane.RTCP.Packet alias Membrane.RTCP.ReportPacketBlock alias Membrane.RTP defstruct [:ssrc, :reports, :sender_info] @type sender_info_t :: %{ wallclock_timestamp: Membrane.Time.t(), rtp_timestamp: non_neg_integer(), sender_packet_count: non_neg_integer(), sender_octet_count: non_neg_integer() } @type t :: %__MODULE__{ ssrc: RTP.ssrc(), reports: [ReportPacketBlock.t()], sender_info: sender_info_t() } @impl true def encode(report) do sender_info = encode_sender_info(report.sender_info) blocks = report.reports |> Enum.map_join(&ReportPacketBlock.encode/1) reports_count = report.reports |> length() body = <> <> sender_info <> blocks {body, reports_count} end defp encode_sender_info(sender_info) do ntp_timestamp = Membrane.Time.to_ntp_timestamp(sender_info.wallclock_timestamp) <> end @impl true def decode( <>, reports_count ) do wallclock_ts = Membrane.Time.from_ntp_timestamp(ntp_timestamp) sender_info = %{ wallclock_timestamp: wallclock_ts, rtp_timestamp: rtp_time, sender_packet_count: packet_count, sender_octet_count: octet_count } with {:ok, reports} <- ReportPacketBlock.decode(blocks), true <- reports_count == length(reports) do {:ok, %__MODULE__{ssrc: ssrc, reports: reports, sender_info: sender_info}} else false -> {:error, :invalid_reports_count} err -> err end end def decode(_packet, _reports_count) do {:error, :sr_too_short} end end