# `XGW.Trunk.Pipe`
[🔗](https://github.com/Lazarus404/xgw/blob/main/lib/xgw/trunk/pipe.ex#L1)

Bidirectional UDP + SRTP trunk between media edges.

## What problem this solves

Multi-home rooms need an SFU-to-SFU media path that is not WebRTC/ICE and not
Distributed Erlang. This GenServer owns one UDP socket, encrypts RTP/RTCP with
a pre-shared key, multiplexes opaque datachannel frames, and delivers inbound
plaintext to a `handler` pid.

## RFCs

* RFC 3711 -- SRTP on RTP/RTCP frames
* RFC 3550 -- RTP payloads after unprotect

# `stats`

```elixir
@type stats() :: %{udp_in: non_neg_integer(), udp_out: non_neg_integer()}
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `connect`

```elixir
@spec connect(pid(), :inet.ip_address(), :inet.port_number()) :: :ok
```

Sets the remote edge UDP address for outbound frames.

# `ensure_label`

```elixir
@spec ensure_label(pid(), binary()) :: {:ok, reference()} | {:error, term()}
```

Registers a label and returns a channel_ref for SendHandle / Forwarder.

# `hot_send_dc`

```elixir
@spec hot_send_dc(pid(), reference(), binary(), :string | :binary) ::
  :ok | {:error, term()}
```

SendHandle datachannel MFA sink (`apply(mod, fun, [pipe, ref, data, type])`).

# `hot_send_rtp`

```elixir
@spec hot_send_rtp(pid(), non_neg_integer(), XMediaLib.Rtp.t()) ::
  :ok | {:error, term()}
```

SendHandle RTP MFA sink (`apply(mod, fun, [pipe, ssrc, rtp])`).

# `local_addr`

```elixir
@spec local_addr(pid()) ::
  {:ok, {:inet.ip_address(), :inet.port_number()}} | {:error, term()}
```

Returns the local `{ip, port}` of the UDP socket.

# `publish_send_handles`

```elixir
@spec publish_send_handles(pid()) :: :ok
```

Publishes RTP + datachannel SendHandle trunk passthrough for this pipe pid.

# `send_dc`

```elixir
@spec send_dc(pid(), binary(), binary()) :: :ok | {:error, term()}
```

Sends an opaque datachannel payload labeled for demux on the far edge.

# `send_rtp`

```elixir
@spec send_rtp(pid(), non_neg_integer(), XMediaLib.Rtp.t() | binary()) ::
  :ok | {:error, term()}
```

Encrypts and sends one RTP packet to the connected peer.

# `set_handler`

```elixir
@spec set_handler(pid(), pid()) :: :ok
```

Replaces the inbound handler pid (e.g. your edge-to-edge bridge process).

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts a trunk pipe.

Opts:

* `:handler` - pid that receives inbound `{__MODULE__, kind, pipe, ...}` messages
* `:master_key` / `:master_salt` - shared lab keys (both directions)
* `:local_write_key` / `:local_write_salt` / `:remote_write_key` /
  `:remote_write_salt` - directional write keys (preferred)
* `:port` - optional bind port (default `0` ephemeral)

# `stats`

```elixir
@spec stats(pid()) :: stats()
```

UDP in/out packet counters (lab assertion that media used UDP).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
