Media tap
A media tap attaches your code to a call's audio — pulling encoded frames out
(IMediaReceiver) and/or pushing frames in (IMediaSender). This is the foundation for
bots, transcription, and streaming to an AI backend, and it is the contract the
commercial modules build on.
Runnable example: CustomAudio attaches a receiver for inbound frame stats and a sender that injects a generated PCMU tone — no audio hardware.
Receive call audio
IMediaReceiver receiver = client.Media.CreateReceiver();
// attach the receiver to the call's media session, then consume the encoded frames
// (e.g. forward to STT, record, analyze) …
Inject audio into a call
IMediaSender sender = client.Media.CreateSender();
await sender.SendAsync(frame); // frame: an encoded MediaFrame (payload in the negotiated codec)
SendAsync(MediaFrame, CancellationToken) paces frames into the call's send path. Feed it
frames already encoded in the negotiated codec — encode your TTS/PCM output first (the
payload is not PCM; see the note below).
Typical bot loop
CreateReceiver()→ inbound frames (encoded in the negotiated codec) → decode → your STT / logic.- Your logic produces a response as audio → encode it into the negotiated codec.
CreateSender().SendAsync(frame)→ the response plays to the remote party.
SRTP/SRTCP is transparent — tapped frames are already decrypted — but the payload is
encoded in the negotiated codec (MediaFrame.PayloadType, e.g. 0 = PCMU), not PCM.
Decode/encode it yourself; the CustomAudio example µ-law-encodes a tone for PCMU.
Headless operation
For a server-side bot you usually do not call AttachDefaultAudioAsync. Skip the
OS device entirely and drive the call through the tap. Set an
InboundMediaTimeout so calls without media
don't linger.
Relationship to modules
The tap is a public, tested contract. Commercial plugins (Realtime, WebSocket, Intelligence) consume exactly this surface via the module registry — and so can your own in-house modules.