media

package
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MPL-2.0 Imports: 31 Imported by: 0

Documentation

Overview

Package media implements the RTP/RTCP/SDP layer used by diago dialogs: session negotiation, secure RTP (SDES and DTLS-SRTP), packet reader/writer handles, jitter buffering and statistics.

Session lifecycle

A MediaSession goes through a Config phase (fields may be set before Init) and an Active phase: after a completed offer/answer exchange the session is FROZEN and only IO is allowed. Any change (codecs, addresses, direction) requires Fork followed by installing the fork through diago's Media.

Negotiation (RemoteSDP/LocalSDP/Finalize) must be funneled through a single goroutine. IO goes through the RTPPacketReader/RTPPacketWriter stable handles, which hot-swap their internals on media updates and carry the refcounted PauseRead/PauseWrite gates; callers resolve the current session at use time instead of capturing it.

RTCP

RTCP uses a separate socket at RTP port + 1; rtcp-mux is not supported. RTPSession.MonitorBackground runs the RTCP reader/writer loops and reports quality via ReadStats/WriteStats and the OnReadRTCP/OnWriteRTCP hooks.

Logging and debug tracing

Every session logs through MediaSession.Logger (falling back to slog.Default), and RTPSession and the packet handles inherit it. RTP/RTCP tracing is per session: MediaSession.RTPDebug/RTCPDebug route packets to MediaSession.Tracer or to the session logger. DTLS handshake tracing is enabled per configuration with DTLSConfig.Debug. RTPBufSize is a constant (MTU guard); packet paths carry no package-wide mutable settings.

Example (Audio2RTPGenerator)

Example on how to generate RTP packets from audio bytes

// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2024, Emir Aganovic

package main

import (
	"bytes"
	"fmt"
	"os"

	"github.com/pion/rtp"
)

type rtpBuffer struct {
	buf []rtp.Packet
}

func (b *rtpBuffer) WriteRTP(p *rtp.Packet) error {
	b.buf = append(b.buf, *p)
	return nil
}

// Example on how to generate RTP packets from audio bytes
func main() {
	// Create some audio
	audioAlawBuf := make([]byte, 4*160)
	copy(audioAlawBuf, bytes.Repeat([]byte("0123456789"), CodecAudioAlaw.Samples16()*2/10))

	// Create Packet writer and pass RTP buff
	rtpBuf := &rtpBuffer{}
	rtpGenerator := NewRTPPacketWriter(rtpBuf, CodecAudioAlaw)
	WriteAll(rtpGenerator, audioAlawBuf, 160)

	// Now we have RTP packets ready to use from audio
	for _, p := range rtpBuf.buf {
		fmt.Fprint(os.Stderr, p.String())
	}
	fmt.Println(len(rtpBuf.buf))
}
Output:
4

Index

Examples

Constants

View Source
const (
	ServerClientAuthNoCert      = int(dtls.NoClientCert)
	ServerClientAuthRequireCert = int(dtls.RequireAnyClientCert)

	EllipticCurveP256   uint16 = uint16(elliptic.P256)
	EllipticCurveP384   uint16 = uint16(elliptic.P384)
	EllipticCurveX25519 uint16 = uint16(elliptic.X25519)
)
View Source
const (
	SRTPProfileAes128CmHmacSha1_80 uint16 = uint16(srtp.ProtectionProfileAes128CmHmacSha1_80)
	SRTPProfileAes256CmHmacSha1_80 uint16 = uint16(srtp.ProtectionProfileAes256CmHmacSha1_80)
	SRTPProfileAeadAes128Gcm       uint16 = uint16(srtp.ProtectionProfileAeadAes128Gcm)
	SRTPProfileAeadAes256Gcm       uint16 = uint16(srtp.ProtectionProfileAeadAes256Gcm)
	SRTPProfileNullHmacSha1_80     uint16 = uint16(srtp.ProtectionProfileNullHmacSha1_80)
)
View Source
const DefaultDTMFVolume uint8 = 10

DefaultDTMFVolume is the signal volume applied when DTMFEncodeOptions marks no explicit choice.

View Source
const RTPBufSize = 1500

RTPBufSize is the size of buffers used on RTP read paths (MTU guard). It is a constant so no packet path depends on process-wide mutable state.

Variables

View Source
var (
	// Here are some codec constants that can be reused
	CodecAudioUlaw          = Codec{PayloadType: 0, SampleRate: 8000, SampleDur: 20 * time.Millisecond, NumChannels: 1, Name: "PCMU"}
	CodecAudioAlaw          = Codec{PayloadType: 8, SampleRate: 8000, SampleDur: 20 * time.Millisecond, NumChannels: 1, Name: "PCMA"}
	CodecAudioOpus          = Codec{PayloadType: 96, SampleRate: 48000, SampleDur: 20 * time.Millisecond, NumChannels: 2, Name: "opus"}
	CodecTelephoneEvent8000 = Codec{PayloadType: 101, SampleRate: 8000, SampleDur: 20 * time.Millisecond, NumChannels: 1, Name: "telephone-event"}
)
View Source
var (
	// ErrReadPaused is returned by RTPPacketReader reads while the reader is
	// paused (PauseRead) or interrupted through a context.
	ErrReadPaused = errors.New("rtp read paused")

	// ErrWritePaused is returned by RTPPacketWriter writes while the writer is
	// paused (PauseWrite).
	ErrWritePaused = errors.New("rtp write paused")
)

Pause/interrupt sentinels for the media read/write gates.

The gates replace the legacy pattern of expressing "stop/pause" by expiring the shared UDP conn deadlines (StopRTP/StartRTP), where multiple components overwrote each other's state. With the gates, the read/write deadline is a transient interrupt signal owned by the stable handles; a pause is a refcounted state resolved at use time.

View Source
var (
	ErrRTPSequenceOutOfOrder = errors.New("out of order")
	ErrRTPSequenceBad        = errors.New("bad sequence")
	ErrRTPSequnceDuplicate   = errors.New("sequence duplicate")
)
View Source
var ErrInvalidPacketDuration = errors.New("media: rtp jitter buffer packetDuration must be greater than zero")

ErrInvalidPacketDuration is returned by NewRTPJitterBuffer when the packet duration derived from the codec is not greater than zero.

View Source
var (

	// ErrRTPSessionClosed is the exported counterpart of errRTPSessionClosed.
	// Exposed so callers can detect the "media already torn down" condition
	// without depending on internal state.
	ErrRTPSessionClosed = errRTPSessionClosed
)

Functions

func CodecsFromSDPRead

func CodecsFromSDPRead(formats []string, attrs []string, codecsAudio []Codec) (int, error)

CodecsFromSDP will try to parse as much as possible, but it will return also error in case some properties could not be read You can take what is parsed or return error

func Copy

func Copy(reader io.Reader, writer io.Writer) (int64, error)

Copy is like io.Copy but it uses buffer size needed for RTP

func CopyContext

func CopyContext(ctx context.Context, reader io.Reader, writer io.Writer) (int64, error)

CopyContext is CopyWithBuf that stops with ctx.Err() when ctx is done. The check runs between reads/writes; a read blocked on the network is expected to be interrupted through the reader's own gate (ReadContext or ArmReadInterrupt), keeping cancellation latency within one packet interval.

func CopyWithBuf

func CopyWithBuf(reader io.Reader, writer io.Writer, payloadBuf []byte) (int64, error)

CopyWithBuf is simple and strict compared to io.CopyBuffer. ReadFrom and WriteTo is not considered and due to RTP buf requirement it can lead to different buffer size passing

func CopyWithBufContext

func CopyWithBufContext(ctx context.Context, reader io.Reader, writer io.Writer, payloadBuf []byte) (int64, error)

CopyWithBufContext is CopyWithBuf with context checks between iterations.

func CurrentNTPTimestamp

func CurrentNTPTimestamp() uint64

func DTMFDecode

func DTMFDecode(payload []byte, d *DTMFEvent) error

DecodeRTPPayload decodes an RTP payload into a DTMF event

func DTMFEncode

func DTMFEncode(d DTMFEvent) []byte

func DTMFToRune

func DTMFToRune(dtmf uint8) rune

func DefaultLogger

func DefaultLogger() *slog.Logger

DefaultLogger returns the fallback logger of the media package. It is used only by sessions without a MediaSession.Logger and by package-level entry points that carry no session (raw DTLS helpers, direct RTPPacketReader construction). Configure per-session loggers (MediaSession.Logger, diago.MediaConfig.Logger) instead of a process-wide one.

func IsDTMFEvent

func IsDTMFEvent(char rune) bool

IsDTMFEvent reports whether char has an RFC 4733 event code ('0'-'9', 'A'-'D', '*', '#').

func NTPTimestamp

func NTPTimestamp(t time.Time) uint64

func OpusSupportedForNegotiation

func OpusSupportedForNegotiation() bool

OpusSupportedForNegotiation is the public gate for tests/docs.

func RTCPUnmarshal

func RTCPUnmarshal(data []byte, packets []rtcp.Packet) (n int, err error)

RTCPUnmarshal is improved version based on pion/rtcp where we allow caller to define and control buffer of rtcp packets. This also reduces one allocation NOTE: data is still referenced in packet buffer

func RTPUnmarshal

func RTPUnmarshal(buf []byte, p *rtp.Packet) error

RTPUnmarshal wrapper for now used for optimizing unmarshal

func ReadAll

func ReadAll(reader io.Reader, sampleSize int) ([]byte, error)

func StringRTCP

func StringRTCP(p rtcp.Packet) string

func ValidateSDPSessionName

func ValidateSDPSessionName(name string) error

ValidateSDPSessionName rejects session names that could break out of the "s=" line: SDP line breaks are CR or LF, so a name carrying them would inject additional SDP attributes. Empty keeps the library default and is valid here; option-level empty checks are callers' responsibility.

func WriteAll

func WriteAll(w io.Writer, data []byte, sampleSize int) (int64, error)

Types

type Codec

type Codec struct {
	Name        string
	PayloadType uint8
	SampleRate  uint32
	SampleDur   time.Duration
	NumChannels int // 1 or 2
}

func CodecAudioFromList

func CodecAudioFromList(codecs []Codec) (Codec, bool)

func CodecAudioFromPayloadType

func CodecAudioFromPayloadType(payloadType uint8) (Codec, error)

func CodecAudioFromSession

func CodecAudioFromSession(s *MediaSession) Codec

func (*Codec) SampleTimestamp

func (c *Codec) SampleTimestamp() uint32

SampleTimestamp returns number of samples as RTP Timestamp measure

func (*Codec) Samples16

func (c *Codec) Samples16() int

Samples16 returns PCM 16 bit samples size

func (*Codec) SamplesPCM

func (c *Codec) SamplesPCM(bitSize int) int

Samples is samples in pcm

func (*Codec) String

func (c *Codec) String() string

type DTLSConfig

type DTLSConfig struct {
	Certificates []tls.Certificate
	// If used as client this would verify server certificate
	ServerName string

	// ServerClientAuth determines the server's policy for
	// TLS Client Authentication. The default is ServerClientAuthNoCert.
	// Check ServerClientAuth
	ServerClientAuth int

	// SRTPProfiles to use in exchange. Check constant vars with media.SRTPProfile...
	SRTPProfiles []uint16

	// SDP Setup Role force value.
	// Values: active,passive,actpass
	// Default: offer->active answer->passive
	SDPSetupRole func(offer bool) string `json:"-"`

	// List of Elliptic Curves to use
	//
	// If an ECC ciphersuite is configured and EllipticCurves is empty
	// it will default to X25519, P-256, P-384 in this specific order.
	// Check values with media.Eliptic<name>
	EllipticCurves []uint16

	// Debug enables DTLS handshake logging (trace level) for sessions using
	// this configuration.
	Debug bool
}

func (*DTLSConfig) ToLibConf

func (conf *DTLSConfig) ToLibConf(fingerprints []sdpFingerprints, log *slog.Logger) *dtls.Config

type DTMFEncodeOptions

type DTMFEncodeOptions struct {
	// Volume is the RFC 4733 signal volume (0-63 relative dBov, 0 being the
	// loudest). It is only honored when VolumeSet is true — the zero value of
	// the struct must keep the legacy default (DefaultDTMFVolume), and volume
	// 0 is itself a legal wire value, so it cannot double as "unset".
	Volume uint8
	// VolumeSet marks Volume as explicitly provided.
	VolumeSet bool
	// EventDuration is the event hold duration reported in the Duration
	// fields. Zero means the default, 80ms.
	EventDuration time.Duration
}

DTMFEncodeOptions tunes the event packet series generated for one digit.

type DTMFEvent

type DTMFEvent struct {
	Event      uint8
	EndOfEvent bool
	Volume     uint8
	Duration   uint16
}

DTMFEvent represents a DTMF event

func RTPDTMFEncode

func RTPDTMFEncode(codec Codec, char rune, opts DTMFEncodeOptions) ([]DTMFEvent, error)

RTPDTMFEncode creates the series of redundant event packets for one digit, scaled to the codec clock rate (160 ticks @8k, 960 @48k per 20ms). A zero/negative codec clock falls back to the canonical 8 kHz telephone-event rate (mirroring the pacing guard in WriteDTMFWithOptions). Layout: 4 active packets with growing Duration, then 3 EndOfEvent packets repeating the final duration.

func (*DTMFEvent) String

func (ev *DTMFEvent) String() string

type MediaSession

type MediaSession struct {

	// Codecs are initial list of Codecs that would be used in SDP generation
	Codecs []Codec

	// Creation-time config normally set from diago's MediaConfig. Set only in
	// the Config phase (before Init); carried through Fork for re-INVITE
	// consistency.
	RTPPortStart             int
	RTPPortEnd               int
	SDPCodecPreferLocalOrder bool
	// SDPSessionName overrides the SDP "s=" line. Empty keeps the default
	// "Sip Go Media". Set only in the Config phase; carried through Fork.
	SDPSessionName string

	// Logger receives this session's logs. Nil falls back to the media
	// package default (slog.Default). Carried through Fork.
	Logger *slog.Logger

	// RTPDebug enables RTP packet tracing on this session. When Tracer is
	// set packets are delivered to it; otherwise they are logged through
	// Logger. Set only in the Config phase; carried through Fork.
	RTPDebug bool
	// RTCPDebug enables RTCP packet logging through Logger. Set only in the
	// Config phase; carried through Fork.
	RTCPDebug bool
	// Tracer receives RTP packets when RTPDebug is enabled. Nil logs instead.
	// Set only in the Config phase; carried through Fork.
	Tracer RTPTracer
	// SAVPDisable keeps RTP/AVP in the offered SDP profile even when SRTP is
	// enabled (a=crypto compatibility mode), unless the remote offer already
	// uses RTP/SAVP. Set only in the Config phase; carried through Fork.
	SAVPDisable bool
	// SourceLock enables RTP source locking after IP changes. Set only in the
	// Config phase; carried through Fork.
	SourceLock bool
	// OnReadRTCP/OnWriteRTCP are the RTCP hooks installed on RTPSession
	// instances created from this session. Nil keeps the previous hook
	// behavior. Set only in the Config phase; carried through Fork.
	OnReadRTCP  func(pkt rtcp.Packet, rtpStats RTPReadStats)
	OnWriteRTCP func(pkt rtcp.Packet, rtpStats RTPWriteStats)

	// Mode is sdp mode. Check consts sdp.ModeRecvOnly etc...
	Mode string
	// Laddr our local address which has full IP and port after media session creation
	Laddr net.UDPAddr
	// Raddr is our target remote address. Normally it is resolved by SDP parsing.
	Raddr net.UDPAddr
	// ExternalIP that should be used for building SDP
	ExternalIP net.IP

	SecureRTP SecureRTP // 0 none, 1 - SDES, 2 - DTLS
	// TODO support multile for offering
	SRTPAlg SRTPAlg

	// DTLSConf used for DTLS
	DTLSConf DTLSConfig

	// RTP NAT enables handling RTP behind NAT. Checkout also MediaSession.SourceLock
	RTPNAT RTPNATMode // 0 - disabled, 1 - Learn source change (RTP Symetric)

	// ReadRTPFromAddr is set after Read operation. NOT THREAD SAFE and should be only used together with Read
	// It can be used to validate source of RTP packet
	ReadRTPFromAddr net.Addr
	// contains filtered or unexported fields
}

func NewMediaSession

func NewMediaSession(ip net.IP, port int) (s *MediaSession, e error)

func (*MediaSession) Close

func (s *MediaSession) Close() error

func (*MediaSession) CodecForPayloadType

func (s *MediaSession) CodecForPayloadType(payloadType uint8) (Codec, bool)

CodecForPayloadType resolves a codec by payload type. The negotiated list (filterCodecs) is searched first so remote-assigned dynamic payload types (ex. opus at PT 111, telephone-event at PT 100) resolve to the negotiated codec instead of failing against locally configured defaults. Falls back to the configured codecs before/without negotiation.

func (*MediaSession) CommonCodecs

func (s *MediaSession) CommonCodecs() []Codec

CommonCodecs returns common codecs if negotiation is finished, that is Local and Remote SDP are exchanged NOTE: Not thread safe, should be called after negotiation Only!

func (*MediaSession) DTMFCodecNegotiated

func (s *MediaSession) DTMFCodecNegotiated() (Codec, bool)

DTMFCodecNegotiated resolves the telephone-event codec from the effective codec list (negotiated filterCodecs when present, otherwise the local list). ok=false means the peer does not support RFC 4733 — callers should fall back to inband tones or SIP INFO. It never invents PT 101.

func (*MediaSession) DiscardQueuedRTP

func (m *MediaSession) DiscardQueuedRTP() (int, error)

DiscardQueuedRTP drains datagrams already queued on the RTP socket and returns how many were discarded. It exists for the moment a media consumer starts (ex. a Bridge about to proxy): without it, whatever arrived while nobody was reading is replayed at the line rate and becomes permanent one-way latency.

The caller must guarantee that no other component is reading this session concurrently - the drain consumes datagrams from the same socket. Packets are dropped raw: no SRTP decryption, RTP/RTCP statistics, RTPNAT source learning or reader-chain taps observe them. Any read deadline is cleared before returning, so normal blocking reads keep working. A nil conn (media never initialized) is a no-op.

On a DTLS-SRTP session the drain is a no-op until the handshake completes (Finalize returns): DTLS flights share the RTP socket with SRTP, so draining before then would eat handshake records. Afterwards the socket carries SRTP only.

func (*MediaSession) Finalize

func (s *MediaSession) Finalize() error

Finalize finalizes negotiation and does verification Should be called only after exchage of SDP is done

func (*MediaSession) Fork

func (s *MediaSession) Fork() *MediaSession

Fork creates a draft copy for a session update (re-INVITE, hold, ...). The draft shares the parent's RTP/RTCP connections; everything negotiated (Raddr, rtcpRaddr, negotiated mode, filterCodecs, SRTP/DTLS state, NAT-learned addresses) is reset and must be re-established via RemoteSDP on the draft. The draft becomes active only when installed through diago's Media mutating the active session in place is forbidden.

An established DTLS-SRTP association is the exception: its keys cannot be re-derived without re-running the handshake (which is impossible while media is flowing on the shared socket), so the SRTP contexts are carried over and the fork keeps encrypting with the existing association (RFC 5763 §6.6).

func (*MediaSession) Init

func (s *MediaSession) Init() error

Init should be called if session is created manually Use NewMediaSession for default building

func (*MediaSession) LocalSDP

func (s *MediaSession) LocalSDP() []byte

LocalSDP generates SDP based on local settings and remote SDP. It is part of the offer/answer flow, not a pure getter: the first call allocates the o= session id, every call increments its version, and on an SRTP offer it creates the local crypto context. SDES setup failures are logged and the SDP continues in plaintext. It must never be called in parallel to RemoteSDP.

func (*MediaSession) NegotiatedDirection

func (s *MediaSession) NegotiatedDirection() string

NegotiatedDirection returns the direction negotiated in the last completed offer/answer exchange: one of sdp.ModeSendrecv, sdp.ModeSendonly, sdp.ModeRecvonly or sdp.ModeInactive. It returns "" before the first negotiation and after a Fork (forks reset it until RemoteSDP applies).

The value is written while the owning Media holds its lock (all RemoteSDP/install paths do); read it under the same discipline. From component code, resolve through Media instead of capturing a session.

func (*MediaSession) ReadRTCP

func (m *MediaSession) ReadRTCP(buf []byte, pkts []rtcp.Packet) (n int, err error)

ReadRTCP is optimized reads and unmarshals RTCP packets. Buffers is only used for unmarshaling. Caller needs to be aware of size this buffer and allign with MTU

func (*MediaSession) ReadRTCPRaw

func (m *MediaSession) ReadRTCPRaw(buf []byte) (int, net.Addr, error)

func (*MediaSession) ReadRTP

func (m *MediaSession) ReadRTP(buf []byte, pkt *rtp.Packet) (int, error)

ReadRTP reads data from network and parses to pkt buffer is passed in order to avoid extra allocs

func (*MediaSession) ReadRTPRaw

func (m *MediaSession) ReadRTPRaw(buf []byte) (int, error)

func (*MediaSession) RemoteSDP

func (s *MediaSession) RemoteSDP(sdpReceived []byte) error

RemoteSDP applies remote SDP. NOTE: It must called ONCE or single thread while negotiation happening. For multi negotiation Fork Must be called before

func (*MediaSession) SetRemoteAddr

func (s *MediaSession) SetRemoteAddr(raddr *net.UDPAddr)

SetRemoteAddr is helper to set Raddr and rtcp address. The RTCP address is derived as RTP port + 1 (rtcp-mux is not supported). It is not thread safe

func (*MediaSession) StartRTP

func (s *MediaSession) StartRTP(rw int8) error

StartRTP clears the conn deadline (both directions when the mask covers them), restoring blocking reads and writes. It is the counterpart of StopRTP; see there for when to prefer the gate APIs.

func (*MediaSession) StopRTP

func (s *MediaSession) StopRTP(rw int8, dur time.Duration) error

StopRTP pauses reading and/or writing by expiring the shared conn deadline. This is a durable, global state on the conn: any later StartRTP clears it for everyone, so concurrent components overwrite each other.

It is a low-level primitive retained for the deadline-based components (bridge mix polling, DTMF read windows). New code should use the refcounted gate APIs on the stable handles: diago's Media.PauseAudioRead / PauseAudioWrite (PauseRead/PauseWrite in this package). The rw mask values 1 (read) and 2 (write) can be combined.

func (*MediaSession) String

func (s *MediaSession) String() string

func (*MediaSession) WriteRTCP

func (m *MediaSession) WriteRTCP(p rtcp.Packet) error

func (*MediaSession) WriteRTCPRaw

func (m *MediaSession) WriteRTCPRaw(data []byte) (int, error)

func (*MediaSession) WriteRTP

func (m *MediaSession) WriteRTP(p *rtp.Packet) error

func (*MediaSession) WriteRTPRaw

func (m *MediaSession) WriteRTPRaw(data []byte) (n int, err error)

type OnRTPReadStats

type OnRTPReadStats func(stats RTPReadStats)

type OnRTPWriteStats

type OnRTPWriteStats func(stats RTPWriteStats)

type RTPDTMFReader

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

func NewRTPDTMFReader

func NewRTPDTMFReader(codec Codec, packetReader *RTPPacketReader, reader io.Reader) *RTPDTMFReader

RTP DTMF reader is middleware for reading DTMF events. It reads from io Reader and checks packet Reader

func (*RTPDTMFReader) Read

func (w *RTPDTMFReader) Read(b []byte) (int, error)

Write is RTP io.Writer which adds more sync mechanism

func (*RTPDTMFReader) ReadDTMF

func (w *RTPDTMFReader) ReadDTMF() (rune, bool)

type RTPDTMFWriter

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

func NewRTPDTMFWriter

func NewRTPDTMFWriter(codec Codec, rtpPacketizer *RTPPacketWriter, writer io.Writer) *RTPDTMFWriter

RTP DTMF writer is middleware for passing RTP DTMF events. If it is chained it is used to block the writer while writing DTMF events.

func (*RTPDTMFWriter) Write

func (w *RTPDTMFWriter) Write(b []byte) (int, error)

Write is RTP io.Writer which adds more sync mechanism

func (*RTPDTMFWriter) WriteDTMF

func (w *RTPDTMFWriter) WriteDTMF(dtmf rune) error

WriteDTMF encodes and sends one RFC 4733 event with defaults (volume 10, 80ms hold). It blocks for the whole event (~7 * codec.SampleDur). A concurrent PauseWrite gate is waited out; use WriteDTMFContext when that wait must be cancellable.

func (*RTPDTMFWriter) WriteDTMFContext

func (w *RTPDTMFWriter) WriteDTMFContext(ctx context.Context, dtmf rune) error

WriteDTMFContext is WriteDTMF with a context: ctx never starts the event once canceled and it interrupts a wait spent behind the PauseWrite gate. An event whose first packet was accepted completes regardless.

func (*RTPDTMFWriter) WriteDTMFWithOptions

func (w *RTPDTMFWriter) WriteDTMFWithOptions(dtmf rune, opts DTMFEncodeOptions) error

WriteDTMFWithOptions sends one event with tuned encoding (volume, event hold). The event packets ride on the packet writer's SSRC with their own timestamp (no audio clock advance), paced one codec.SampleDur per packet. It works at any telephone-event clock rate (8k/16k/32k/48k per RFC 4733).

func (*RTPDTMFWriter) WriteDTMFWithOptionsContext

func (w *RTPDTMFWriter) WriteDTMFWithOptionsContext(ctx context.Context, dtmf rune, opts DTMFEncodeOptions) error

WriteDTMFWithOptionsContext is WriteDTMFWithOptions with a context. A canceled context never starts the event, and it cuts short a wait on a paused write gate (returning ctx.Err()); once the first packet is accepted the event completes, so cancellation latency is one event with the gate open and immediate while it stays closed.

type RTPExtendedSequenceNumber

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

RTPExtendedSequenceNumber is embedable/ replacable sequnce number generator For thread safety you should wrap it

func NewRTPExtendedSequenceNumber

func NewRTPExtendedSequenceNumber() RTPExtendedSequenceNumber

func (*RTPExtendedSequenceNumber) InitSeq

func (sn *RTPExtendedSequenceNumber) InitSeq(seq uint16)

func (*RTPExtendedSequenceNumber) NextSeqNumber

func (s *RTPExtendedSequenceNumber) NextSeqNumber() uint16

func (*RTPExtendedSequenceNumber) ReadExtendedSeq

func (sn *RTPExtendedSequenceNumber) ReadExtendedSeq() uint64

func (*RTPExtendedSequenceNumber) UpdateSeq

func (sn *RTPExtendedSequenceNumber) UpdateSeq(seq uint16) error

Based on https://datatracker.ietf.org/doc/html/rfc1889#appendix-A.2

type RTPJitterBuffer

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

RTPJitterBuffer is a fixed-delay, single-consumer RTPReader wrapper.

It sits after RTPSession and before RTPPacketReader, so RTPSession observes true network arrival while downstream readers get reordered packets.

func NewRTPJitterBuffer

func NewRTPJitterBuffer(reader RTPReader, packetDuration time.Duration, opts RTPJitterBufferOptions) (*RTPJitterBuffer, error)

NewRTPJitterBuffer creates a fixed jitter buffer over another RTPReader. It returns ErrInvalidPacketDuration if packetDuration is not greater than zero.

func (*RTPJitterBuffer) Close

func (j *RTPJitterBuffer) Close() error

Close stops jitter-buffer delivery. It does not close the injected reader.

func (*RTPJitterBuffer) ReadRTP

func (j *RTPJitterBuffer) ReadRTP(buf []byte, p *rtp.Packet) (int, error)

ReadRTP implements RTPReader. Calls must not overlap.

func (*RTPJitterBuffer) Statistics

Statistics returns a race-safe snapshot of jitter buffer counters.

type RTPJitterBufferOptions

type RTPJitterBufferOptions struct {
	// DelayPackets is the initial fixed playout delay in packets. If unset, 3 is used.
	DelayPackets int
	// MaxPackets caps buffered packets and the forward reordering window. If unset, 10 is used.
	MaxPackets int
}

RTPJitterBufferOptions configures a fixed RTP jitter buffer.

type RTPJitterBufferStatistics

type RTPJitterBufferStatistics struct {
	PacketsRead        uint64
	PacketsReleased    uint64
	PacketsLost        uint64
	PacketsLate        uint64
	PacketsDuplicate   uint64
	PacketsDropped     uint64
	SSRCResets         uint64
	LastSequenceNumber uint16
}

RTPJitterBufferStatistics contains simple counters for buffer decisions.

type RTPNATMode

type RTPNATMode int

RTPNATMode selects how RTP behind NAT is handled.

const (
	RTPNATDisabled RTPNATMode = iota
	RTPNATLearnSource
	// RTPNATSymetric is the historical name for RTPNATLearnSource.
	RTPNATSymetric = RTPNATLearnSource
)

RTPNAT options.

type RTPPacketReader

type RTPPacketReader struct {

	// PacketHeader is stored after calling Read
	// Safe to read only in same goroutine as Read
	PacketHeader rtp.Header
	// contains filtered or unexported fields
}

RTPPacketReader reads RTP packet and extracts payload and header

func NewRTPPacketReader

func NewRTPPacketReader(reader RTPReader, codec Codec) *RTPPacketReader

func NewRTPPacketReaderSession

func NewRTPPacketReaderSession(sess *RTPSession) *RTPPacketReader

NewRTPPacketReaderSession just helper constructor

func (*RTPPacketReader) ArmReadInterrupt

func (r *RTPPacketReader) ArmReadInterrupt(ctx context.Context) (disarm func())

ArmReadInterrupt arranges for an in-flight read to be interrupted when ctx is done. The returned disarm joins the watcher and, when the interrupt was delivered, restores the reader (unless a pause owns the state). Disarm must be called exactly once; it is safe to call after ctx is already done.

func (*RTPPacketReader) PauseRead

func (r *RTPPacketReader) PauseRead() (release func())

PauseRead pauses the reader: current and subsequent Read calls return ErrReadPaused until the returned release is called.

Pause is refcounted: the reader stays paused until every release is called, so concurrent components can not accidentally resume each other (this is the fix for the legacy StartRTP "resume stomps everyone" behavior). Release must be called exactly once per PauseRead call.

The pause is delivered to the active reader: pause-aware readers (ex. RTPJitterBuffer) are signaled directly, session-owned readers are interrupted by a one-shot read deadline poke which the last release restores. Underlying network buffering is not stopped by a pause.

func (*RTPPacketReader) Read

func (r *RTPPacketReader) Read(b []byte) (int, error)

Read Implements io.Reader and extracts Payload from RTP packet has no input queue or sorting control of packets Buffer is used for reading headers and after parsing Headers are stored in PacketHeader

NOTE: Consider that if you are passsing smaller buffer than RTP header+payload, io.ErrShortBuffer is returned

func (*RTPPacketReader) ReadContext

func (r *RTPPacketReader) ReadContext(ctx context.Context, b []byte) (int, error)

ReadContext reads the next payload, interrupting the underlying blocking read when ctx is done. On cancellation it returns ctx.Err(). The reader stays fully usable afterwards.

func (*RTPPacketReader) Reader

func (r *RTPPacketReader) Reader() RTPReader

func (*RTPPacketReader) UpdateRTPSession

func (r *RTPPacketReader) UpdateRTPSession(rtpSess *RTPSession)

func (*RTPPacketReader) UpdateReader

func (r *RTPPacketReader) UpdateReader(reader RTPReader)

type RTPPacketWriter

type RTPPacketWriter struct {

	// After each write packet header is saved for more reading
	PacketHeader rtp.Header

	// SSRC is readOnly and it is not changed
	SSRC uint32
	// contains filtered or unexported fields
}

RTPPacketWriter packetize any payload before pushing to active media session It creates SSRC as identifier and all packets sent will be with this SSRC For multiple streams, multiple RTP Writer needs to be created

func NewRTPPacketWriter

func NewRTPPacketWriter(writer RTPWriter, codec Codec) *RTPPacketWriter

RTPPacketWriter packetizes payload in RTP packet before passing on media session Not having: - allow different clock rate - CSRC contribution source - Silence detection and marker set updateClockRate- Padding and encryyption

func NewRTPPacketWriterSession

func NewRTPPacketWriterSession(sess *RTPSession) *RTPPacketWriter

NewRTPPacketWriterSession creates RTPPacketWriter and attaches RTP Session expected values

func (*RTPPacketWriter) Close

func (w *RTPPacketWriter) Close() error

Close stops the media clock ticker, releasing the pacing timer when the write pipeline is torn down. Idempotent.

Writes parked waiting for the next tick are woken through the stop channel so they observe the teardown (their packet write fails on the closed conn) instead of waiting for a tick that will never come.

func (*RTPPacketWriter) InitTimestamp

func (p *RTPPacketWriter) InitTimestamp() uint32

InitTimestamp returns init RTP timestamp

func (*RTPPacketWriter) PauseWrite

func (p *RTPPacketWriter) PauseWrite() (release func())

PauseWrite pauses the writer: Write and WriteSamples return ErrWritePaused until the returned release is called. Refcounted like PauseRead; release must be called exactly once per PauseWrite call.

An in-flight write completes first (pacing wait is at most one packet interval), so pause latency is bounded by the codec sample duration.

func (*RTPPacketWriter) ResetTimestamp

func (p *RTPPacketWriter) ResetTimestamp()

ResetTimestamp can mark new stream comming. If stream is continuous it will add timestamp difference MUST Not be called during stream Write

func (*RTPPacketWriter) UpdateRTPSession

func (w *RTPPacketWriter) UpdateRTPSession(rtpSess *RTPSession)

UpdateRTPSession updates the RTP writer when a new RTP session is created.

func (*RTPPacketWriter) Write

func (p *RTPPacketWriter) Write(b []byte) (int, error)

Write implements io.Writer and does payload RTP packetization Media clock rate is determined For more control or dynamic payload WriteSamples can be used Concurrent writes are serialized with internal lock, but payload frame order is a caller responsibility

func (*RTPPacketWriter) WriteSamples

func (p *RTPPacketWriter) WriteSamples(payload []byte, sampleRateTimestamp uint32, marker bool, payloadType uint8) (int, error)

WriteSamples allows to skip default packet rate. This is useful if you need to write different payload but keeping same SSRC

type RTPReadStats

type RTPReadStats struct {
	SSRC                   uint32
	FirstPktSequenceNumber uint16
	LastSequenceNumber     uint16

	// tracks first pkt seq in this interval to calculate loss of packets.
	// Ext variant keeps cycles, so interval math survives sequence wrap
	IntervalFirstPktSeqNum    uint16
	IntervalFirstPktSeqNumExt uint64
	IntervalPacketsCount      uint16

	PacketsCount uint64
	OctetCount   uint64

	// RTP reading stats
	SampleRate uint32

	// Round TRIP Time based on LSR and DLSR
	RTT time.Duration
	// contains filtered or unexported fields
}

Some of fields here are exported (as readonly) intentionally

type RTPReader

type RTPReader interface {
	ReadRTP(buf []byte, p *rtp.Packet) (int, error)
}

type RTPRealTimeReader

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

func NewRTPRealTimeReader

func NewRTPRealTimeReader(reader io.Reader, rtpReader *RTPPacketReader, codec Codec) (*RTPRealTimeReader, error)

func (*RTPRealTimeReader) Init

func (r *RTPRealTimeReader) Init(reader io.Reader, rtpReader *RTPPacketReader, codec Codec) error

func (*RTPRealTimeReader) Read

func (r *RTPRealTimeReader) Read(b []byte) (int, error)

func (*RTPRealTimeReader) Reset

func (r *RTPRealTimeReader) Reset()

type RTPSession

type RTPSession struct {
	// Keep pointers at top to reduce GC
	Sess *MediaSession
	// contains filtered or unexported fields
}

func NewRTPSession

func NewRTPSession(sess *MediaSession) *RTPSession

RTP session creates new RTP reader/writer from session

func (*RTPSession) Close

func (s *RTPSession) Close() error

func (*RTPSession) Fork

func (s *RTPSession) Fork(sess *MediaSession) *RTPSession

Fork creates a new RTP session lifecycle for a forked media session while preserving RTP and RTCP statistics.

func (*RTPSession) Monitor

func (s *RTPSession) Monitor() error

Monitor starts reading RTCP and monitoring media quality

func (*RTPSession) MonitorBackground

func (s *RTPSession) MonitorBackground() error

MonitorBackground is helper to keep monitoring in background MUST Be called after session REMOTE SDP is parsed

func (*RTPSession) MonitorClose

func (s *RTPSession) MonitorClose() error

MonitorClose stops RTCP monitoring and waits until all monitor goroutines have exited. The RTP/RTCP connections remain open for a replacement fork.

func (*RTPSession) OnReadRTCP

func (s *RTPSession) OnReadRTCP(f func(pkt rtcp.Packet, rtpStats RTPReadStats))

func (*RTPSession) OnWriteRTCP

func (s *RTPSession) OnWriteRTCP(f func(pkt rtcp.Packet, rtpStats RTPWriteStats))

func (*RTPSession) ReadRTP

func (s *RTPSession) ReadRTP(b []byte, readPkt *rtp.Packet) (n int, err error)

ReadRTP reads RTP NOTE: For RTCP we may read some properties of media session. Do not run this until full media session is negotiated. For updating media, media session forking must be done!

func (*RTPSession) ReadRTPRaw

func (s *RTPSession) ReadRTPRaw(buf []byte) (int, error)

func (*RTPSession) ReadStats

func (s *RTPSession) ReadStats() RTPReadStats

func (*RTPSession) WriteRTP

func (s *RTPSession) WriteRTP(pkt *rtp.Packet) error

func (*RTPSession) WriteStats

func (s *RTPSession) WriteStats() RTPWriteStats

type RTPStatsReader

type RTPStatsReader struct {
	// Reader should be your AudioReade or any other interceptor RTP reader that is reading audio stream
	Reader io.Reader
	// RTPSession is the statistics source. It is ignored when Session is set.
	RTPSession *RTPSession
	// Session, when set, resolves the statistics source at read time and takes
	// precedence over RTPSession. Use it for pipelines that survive media
	// updates: a re-INVITE swaps the dialog's RTP session without replacing
	// the component holding this interceptor.
	Session func() *RTPSession
	// OnRTPReadStats is fired each time on Read RTP. Must not block
	OnRTPReadStats OnRTPReadStats
}

RTPStatsReader reports RTP read statistics for every successfully read packet.

func (*RTPStatsReader) Read

func (i *RTPStatsReader) Read(b []byte) (int, error)

type RTPStatsWriter

type RTPStatsWriter struct {
	// Writer should be your Writer or any other interceptor RTP writer that is reading audio stream
	Writer io.Writer
	// RTPSession is the statistics source. It is ignored when Session is set.
	RTPSession *RTPSession
	// Session, when set, resolves the statistics source at write time and
	// takes precedence over RTPSession.
	Session func() *RTPSession
	// OnRTPWriteStats is fired each time on Read RTP. Must not block
	OnRTPWriteStats OnRTPWriteStats
}

RTPStatsWriter reports RTP write statistics for every successfully written packet.

func (*RTPStatsWriter) Write

func (i *RTPStatsWriter) Write(p []byte) (int, error)

type RTPTracer

type RTPTracer interface {
	RTPTraceRead(laddr string, raddr string, packet *rtp.Packet)
	RTPTraceWrite(laddr string, raddr string, packet *rtp.Packet)
}

RTPTracer receives decoded RTP packets when MediaSession.RTPDebug is enabled and a Tracer is configured.

Traces run synchronously on the media path. Implementations must not block and must clone packets that are retained after the method returns.

type RTPWriteStats

type RTPWriteStats struct {
	SSRC uint32

	// RTCP stats
	PacketsCount uint64
	OctetCount   uint64
	// contains filtered or unexported fields
}

Some of fields here are exported (as readonly) intentionally

type RTPWriter

type RTPWriter interface {
	WriteRTP(p *rtp.Packet) error
}

type SRTPAlg

type SRTPAlg uint16

SRTPAlg is an SRTP protection profile.

type SecureRTP

type SecureRTP int

SecureRTP selects the SRTP keying mode of a media session.

const (
	// SecureRTPNone disables SRTP.
	SecureRTPNone SecureRTP = iota
	// SecureRTPSDES selects SDES keying.
	SecureRTPSDES
	// SecureRTPDTLS selects DTLS-SRTP.
	SecureRTPDTLS
)

Directories

Path Synopsis
Package sdp implements the SDP subset needed for SIP media negotiation: parsing of SessionDescription values, media/connection lookup, direction attributes and the payload-type constants used by the media package.
Package sdp implements the SDP subset needed for SIP media negotiation: parsing of SessionDescription values, media/connection lookup, direction attributes and the payload-type constants used by the media package.

Jump to

Keyboard shortcuts

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