pion/webrtc · error
duplicate Ogg track SSRC
Error message
duplicate Ogg track SSRC
What it means
errDuplicateTrackSSRC is returned by Writer.NewTrack when a track with the same RTP SSRC already exists in the Ogg writer. Each Ogg logical stream (track) must have a unique SSRC to keep page streams distinguishable on demux.
Source
Thrown at pkg/media/oggwriter/oggwriter.go:51
channelMappingFamily0 = 0
channelMappingFamily1 = 1
channelMappingFamily2 = 2
channelMappingFamily255 = 255
idPageSignature = "OpusHead"
commentPageSignature = "OpusTags"
defaultVendor = "pion"
pageHeaderSignature = "OggS"
pageHeaderSize = 27
maxOggPageSegments = 255
noGranulePosition = ^uint64(0)
maxUint32Length = uint64(1<<32 - 1)
)
var (
errFileNotOpened = errors.New("file not opened")
errOutputNotOpened = errors.New("output not opened")
errInvalidNilPacket = errors.New("invalid nil packet")
errDuplicateTrackSSRC = errors.New("duplicate Ogg track SSRC")
errDuplicateTrackSerial = errors.New("duplicate Ogg track serial")
errTracksStarted = errors.New("cannot add Ogg tracks after writing has started")
errPacketSSRCMismatch = errors.New("RTP packet SSRC does not match Ogg track SSRC")
errInvalidOpusPacket = errors.New("invalid Opus packet")
errInvalidChannelCount = errors.New("invalid channel count")
errInvalidChannelMap = errors.New("invalid channel mapping")
errInvalidOpusTags = errors.New("invalid OpusTags")
)
type pageRewriter interface {
io.Seeker
io.WriterAt
}
type writerConfig struct {
sampleRate uint32
channelMapping channelMapping
pageRewriter pageRewriterView on GitHub (pinned to 8c25dc09fa)
Solutions
- Use a unique SSRC per track; generate one (e.g. random uint32) per NewTrack call
- Look up the existing track by SSRC and reuse it instead of creating a new one
- If the remote SSRC genuinely changed, remove/close the old track first
Example fix
// before
writer.NewTrack(1111)
writer.NewTrack(1111) // errDuplicateTrackSSRC
// after
if _, exists := tracks[1111]; !exists {
writer.NewTrack(1111)
} Defensive patterns
Strategy: validation
Validate before calling
if ssrc == existingSSRC {
return fmt.Errorf("SSRC %d already registered as a track", ssrc)
}
track, err := writer.NewTrack(ssrc, opts...) Type guard
func ssrcInUse(tracks map[uint32]struct{}, ssrc uint32) bool {
_, ok := tracks[ssrc]
return ok
} Try / catch
track, err := writer.NewTrack(ssrc, opts...)
if errors.Is(err, oggwriter.ErrDuplicateTrackSSRC) {
// reuse the existing track for this SSRC instead of failing
} Prevention
- Keep a registry of active SSRCs and check before NewTrack
- Generate SSRCs randomly per RFC 3550 to avoid collisions
- Handle renegotiation by reusing tracks keyed by SSRC
When it happens
Trigger: Calling writer.NewTrack(1111, ...) twice with the same SSRC (oggwriter.go:438 lookup in w.tracks); asserted in oggwriter_test.go:1151 with WithSerial(0x05060708).
Common situations: Re-adding the same remote track after a renegotiation, copying track SSRCs from two peers that collided, hardcoding the same SSRC for multiple audio streams.
Related errors
- RTP packet SSRC does not match Ogg track SSRC
- invalid nil packet
- duplicate Ogg track serial
- stream is nil
- bad header signature
AI-assisted analysis of pion/webrtc@8c25dc09fa (2026-09-03).
Data as JSON: /api/errors/4a64f527a44fb835.
Report an issue: GitHub.