media

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 29, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package media is the RTP side of a call: G.711 audio sent from a shared scheduler, RFC 3550 receive statistics, RFC 4733 DTMF and detection of whether any audio arrives at all.

Index

Constants

This section is empty.

Variables

View Source
var (
	PCMU = Codec{Name: "PCMU", PT: 0, /* contains filtered or unexported fields */}
	PCMA = Codec{Name: "PCMA", PT: 8, /* contains filtered or unexported fields */}
)
View Source
var DefaultAudio = sync.OnceValue(func() *Audio { return Tone(1000, -20, time.Second) })

DefaultAudio is what calls send unless told otherwise: a 1 kHz tone at -20 dBFS, so the far end can tell that audio arrives.

Functions

func AlawDecode

func AlawDecode(a byte) int16

func AlawEncode

func AlawEncode(sample int16) byte

AlawEncode converts a 16-bit linear sample to A-law.

func UlawDecode

func UlawDecode(u byte) int16

UlawDecode and AlawDecode convert one G.711 byte to a linear sample.

func UlawEncode

func UlawEncode(sample int16) byte

UlawEncode converts a 16-bit linear sample to μ-law.

Types

type Audio

type Audio struct {
	// contains filtered or unexported fields
}

Audio is an 8 kHz mono source that streams loop over. It is encoded once per codec and shared read-only by all streams, so thousands of calls cost no encoding CPU.

func ParseWAV

func ParseWAV(data []byte) (*Audio, error)

ParseWAV reads a RIFF WAV file with 16-bit PCM, 8 kHz, mono samples.

func Silence

func Silence() *Audio

Silence is digital silence.

func Tone

func Tone(freq, dbfs float64, dur time.Duration) *Audio

Tone is a sine wave at freq Hz and level dBFS (e.g. -20).

func (*Audio) Duration

func (a *Audio) Duration() time.Duration

Duration of one loop of the source.

func (*Audio) PCM added in v0.4.0

func (a *Audio) PCM() []int16

PCM returns the samples of the source.

type Codec

type Codec struct {
	Name string // as in rtpmap: PCMU, PCMA
	PT   uint8  // static payload type
	// contains filtered or unexported fields
}

Codec is an audio codec we can send and analyse. Only 8 kHz G.711 for now.

func CodecByName

func CodecByName(name string) (Codec, error)

CodecByName accepts "PCMU"/"PCMA" and the common aliases "ulaw"/"alaw".

func ParseCodecs

func ParseCodecs(list string) ([]Codec, error)

ParseCodecs parses a preference list like "PCMA,PCMU".

type Config

type Config struct {
	IP        string  // local address for the socket and the SDP
	Codecs    []Codec // preference order; default PCMU, PCMA
	Audio     *Audio  // what we send; default DefaultAudio()
	Scheduler *Scheduler
	// HeardLevel is the level in dBFS above which a received frame counts
	// as audio; default -45.
	HeardLevel float64
	// NoLatch disables symmetric RTP: by default we send to where the
	// peer's packets come from once the first one arrives (NAT, SBCs).
	NoLatch bool
	// Record keeps the heard and sent audio for Recording().
	Record bool
}

type Direction

type Direction string

Direction is the SDP media direction attribute.

const (
	SendRecv Direction = "sendrecv"
	SendOnly Direction = "sendonly"
	RecvOnly Direction = "recvonly"
	Inactive Direction = "inactive"
)

func (Direction) Reverse

func (d Direction) Reverse() Direction

Reverse is the direction the answerer uses for an offered direction.

type NetStats added in v0.4.0

type NetStats struct {
	PacketsIn, PacketsOut uint64
	BytesIn, BytesOut     uint64
}

NetStats are traffic counters of all RTP sockets of the process.

func RTPNetStats added in v0.4.0

func RTPNetStats() NetStats

RTPNetStats returns the RTP traffic of the process so far, including packets that were not RTP (RTCP, STUN) on the media sockets.

type Quality added in v0.4.0

type Quality struct {
	// Score is the similarity of the heard audio to the reference, 0..1:
	// the correlation of their telephone-band spectrograms after alignment.
	// It ignores the level; drop-outs, noise, distortion and wrong audio
	// lower it.
	Score float64
	// Offset is where the reference starts in the recording.
	Offset time.Duration
	// Compared is the length of the reference that was compared: shorter
	// than the reference when the recording ends early.
	Compared time.Duration
	// Gaps is the reference speech during which nothing was heard.
	Gaps time.Duration
	// ClippedStart is the part of the first word that was lost: gaps from
	// the start of the reference speech until audio is first heard.
	ClippedStart time.Duration
	// Gain is the level of the heard audio relative to the reference, dB.
	Gain float64
}

Quality compares what a call leg heard with the reference audio that should have arrived.

func Compare added in v0.4.0

func Compare(ref, heard []int16) (Quality, bool)

Compare aligns heard with ref and scores it. ok is false when either signal is too short to compare (under one spectrogram frame) or ref has no audio at all.

type Recording added in v0.4.0

type Recording struct {
	Heard []int16 // received audio, what the subscriber hears
	Sent  []int16 // our audio, what the subscriber says
}

Recording is a copy of what a call leg heard and sent.

func (Recording) WriteWAV added in v0.4.0

func (r Recording) WriteWAV(w io.Writer) error

WriteWAV writes the recording as a 16-bit PCM 8 kHz stereo WAV: the left channel is what the subscriber heard, the right what it sent. The shorter track is padded with silence.

type Remote

type Remote struct {
	Addr   *net.UDPAddr // nil or unspecified IP means hold
	Codecs []Codec      // supported codecs in the peer's order
	DTMFPT int          // telephone-event payload type, -1 if not offered
	Dir    Direction
}

Remote is what we learnt from the peer's SDP.

func ParseSDP

func ParseSDP(body []byte) (Remote, error)

ParseSDP extracts the first audio stream of an SDP body.

type Scheduler

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler paces all sending streams from a few goroutines instead of one ticker per stream: with thousands of calls, per-stream tickers make the generator itself produce jitter. Streams are spread over shards; each shard ticks every 20 ms and sends one packet per stream.

func NewScheduler

func NewScheduler(n int) *Scheduler

NewScheduler starts n shards; n <= 0 means GOMAXPROCS.

func (*Scheduler) Close

func (s *Scheduler) Close()

Close stops the shard goroutines.

type Stats

type Stats struct {
	Codec           string
	PacketsSent     uint64
	PacketsReceived uint64
	PacketsExpected uint64
	PacketsLost     uint64
	Jitter          time.Duration
	Heard           time.Duration // time of received audio above HeardLevel
	DTMF            string        // RFC 4733 digits received
}

Stats summarise one stream. Loss and jitter follow RFC 3550.

type Stream

type Stream struct {
	// contains filtered or unexported fields
}

func New

func New(cfg Config) (*Stream, error)

New opens the RTP socket and starts receiving right away, so early media and the first packets after 200 OK are counted.

func (*Stream) Answer

func (s *Stream) Answer(r Remote) ([]byte, error)

Answer negotiates against the peer's offer and returns our answer.

func (*Stream) ApplyAnswer

func (s *Stream) ApplyAnswer(r Remote) error

ApplyAnswer takes the peer's answer to our offer.

func (*Stream) Close

func (s *Stream) Close() Stats

Close stops sending and receiving and returns final statistics.

func (*Stream) Codec

func (s *Stream) Codec() string

Codec is the negotiated codec name, "" before negotiation.

func (*Stream) Digits

func (s *Stream) Digits() string

Digits returns the DTMF digits received so far.

func (*Stream) Direction added in v0.3.0

func (s *Stream) Direction() Direction

Direction is our current media direction.

func (*Stream) Offer

func (s *Stream) Offer(dir Direction) []byte

Offer is our SDP offer with all our codecs.

func (*Stream) Port

func (s *Stream) Port() int

func (*Stream) Recording added in v0.4.0

func (s *Stream) Recording() (Recording, bool)

Recording returns a copy of the recorded audio so far; ok is false when the stream was created without Record.

func (*Stream) SendDTMF

func (s *Stream) SendDTMF(digits string, dur time.Duration) error

SendDTMF queues RFC 4733 events; each digit lasts dur (default 100 ms) followed by a 60 ms pause. It returns once queued.

func (*Stream) SetDirection

func (s *Stream) SetDirection(d Direction)

SetDirection changes what we send/receive, e.g. sendonly for hold.

func (*Stream) Start

func (s *Stream) Start()

Start begins sending (when our direction allows it).

func (*Stream) Stats

func (s *Stream) Stats() Stats

func (*Stream) Update

func (s *Stream) Update(r Remote) []byte

Update applies an in-dialog offer (re-INVITE, e.g. hold) and returns our answer, keeping the negotiated codec.

func (*Stream) WaitDigits

func (s *Stream) WaitDigits(want string, timeout time.Duration) bool

WaitDigits waits until the received digits contain want.

func (*Stream) WaitHeard

func (s *Stream) WaitHeard(timeout time.Duration) bool

WaitHeard waits until at least 100 ms of audio above HeardLevel arrived.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL