if Code.ensure_loaded?(ExLibSRTP) do defmodule Membrane.SRTCP.Decryptor do @moduledoc """ Converts SRTCP packets to plain RTCP. Requires adding [libsrtp](https://github.com/membraneframework/elixir_libsrtp) dependency to work. """ use Membrane.Filter require Membrane.Logger alias Membrane.Buffer alias Membrane.SRTP def_input_pad :input, accepted_format: _any, flow_control: :auto def_output_pad :output, accepted_format: _any, flow_control: :auto def_options policies: [ spec: [ExLibSRTP.Policy.t()], description: """ List of SRTP policies to use for decrypting packets. See `t:ExLibSRTP.Policy.t/0` for details. """ ] @impl true def handle_init(_ctx, options) do {[], Map.from_struct(options) |> Map.merge(%{srtp: nil})} end @impl true def handle_setup(_ctx, state) do srtp = ExLibSRTP.new() state.policies |> Bunch.listify() |> Enum.each(&ExLibSRTP.add_stream(srtp, &1)) {[], %{state | srtp: srtp}} end @impl true def handle_buffer(:input, buffer, _ctx, state) do case ExLibSRTP.unprotect_rtcp(state.srtp, buffer.payload) do {:ok, payload} -> {[buffer: {:output, %Buffer{buffer | payload: payload}}], state} {:error, reason} when reason in [:replay_fail, :replay_old] -> Membrane.Logger.debug(""" Couldn't unprotect srtcp packet: #{inspect(buffer.payload, limit: :infinity)} Reason: #{inspect(reason)}. Ignoring packet. """) {[], state} {:error, reason} -> raise "Couldn't unprotect SRTCP packet due to #{inspect(reason)}" end end @impl true def handle_event(_pad, %SRTP.KeyingMaterialEvent{} = event, _ctx, %{policies: []} = state) do {:ok, crypto_profile} = ExLibSRTP.Policy.crypto_profile_from_dtls_srtp_protection_profile( event.protection_profile ) policy = %ExLibSRTP.Policy{ ssrc: :any_inbound, key: event.remote_keying_material, rtp: crypto_profile, rtcp: crypto_profile } :ok = ExLibSRTP.add_stream(state.srtp, policy) {[], %{state | policies: [policy]}} end @impl true def handle_event(_pad, %SRTP.KeyingMaterialEvent{}, _ctx, state) do Membrane.Logger.warning("Got unexpected SRTP.KeyingMaterialEvent. Ignoring.") {[], state} end @impl true def handle_event(pad, event, ctx, state), do: super(pad, event, ctx, state) end end